├── XenBootFix
├── XenBootFix.rc
├── resource.h
├── scripts
│ ├── sign.ps1
│ └── genfiles.ps1
├── XenBootFix.vcxproj.filters
└── RegHive.h
├── installer
├── branding
│ ├── Up.ico
│ ├── New.ico
│ ├── info.ico
│ ├── xen.ico
│ ├── DlgBmp.bmp
│ ├── BannrBmp.bmp
│ └── exclamic.ico
├── Include.wxi
├── Folders.wxs
├── Tweaks.en-us.wxl
├── scripts
│ ├── sign.ps1
│ └── genfiles.ps1
├── TimeProvider.en-us.wxl
├── Tools.en-us.wxl
├── GuestAgent.en-us.wxl
├── Drivers
│ ├── Xenhid.wxs
│ ├── Xenvkbd.wxs
│ ├── Xenvif.wxs
│ ├── Xencons.wxs
│ ├── Xeniface.wxs
│ ├── Xennet.wxs
│ ├── Xenvbd.wxs
│ └── Xenbus.wxs
├── Tools.wxs
├── Tweaks.wxs
├── Branding.wxs
├── UI
│ ├── Detected3PStorageDriversDlg.wxs
│ ├── XenCustomizeDlg.wxs
│ └── XenDriversUI.wxs
├── Drivers.en-us.wxl
├── TimeProvider.wxs
├── Drivers.wxs
├── XenDrivers.wixproj
├── Package.en-us.wxl
├── InstCA.wxs
├── GuestAgent.wxs
├── Driver.wxi
├── Package.wxs
└── installer.sln
├── XenDriverUtils
├── NativeMethods.json
├── Logger.cs
├── scripts
│ ├── sign.ps1
│ └── genfiles.ps1
├── NativeMethods.txt
├── VersionUtils.cs
├── XenDriverUtils.csproj
├── XenOffboard.cs
├── InfFile.cs
├── XenCleanup.cs
└── Copy-XenVifSettings.ps1
├── XenClean
├── UninstallDrivers.cs
├── Logger.cs
├── UninstallRegistry.cs
├── Program.cs
├── app.manifest
├── UninstallServices.cs
├── scripts
│ ├── sign.ps1
│ └── genfiles.ps1
├── XenClean.csproj
├── UninstallDevices.cs
├── Invoke-XenClean.ps1
└── UninstallProducts.cs
├── scripts
├── xen-guest-agent
│ ├── build.rs
│ └── genfiles.ps1
├── signer-ci.ps1
├── Build-DriverSbom.ps1
├── Compress-DriverCabinet.ps1
├── sign.ps1
├── Test-BuildAttestation.ps1
├── Get-DriverSubmodule.ps1
├── Compare-DriverSource.ps1
├── Test-DriverInf.ps1
├── branding-generic.ps1
├── artifact.psm1
└── branding-ci.ps1
├── DriverInstallCustomAction
├── scripts
│ ├── sign.ps1
│ └── genfiles.ps1
├── CustomActionUtils.cs
├── MsiLogger.cs
├── CustomAction.config
├── DriverInstallCustomAction.csproj
├── CleanupActions.cs
├── ImmediateActions.cs
└── DriverActions.cs
├── .github
├── dependabot.yml
└── workflows
│ ├── attest.yml
│ ├── build-drivers.yml
│ ├── build-timeprovider.yml
│ ├── build-guestagent.yml
│ └── build-installer.yml
├── nuget.config
├── docs
├── offboard.md
├── coexistence.md
├── build.md
└── attestation.md
├── testsign
└── install.ps1
├── .gitmodules
├── SBUpdate
├── SBUpdate.vcxproj.filters
└── SBUpdate.hpp
├── README.md
├── extras
└── xsa468.xml
├── .editorconfig
└── .gitignore
/XenBootFix/XenBootFix.rc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcp-ng/win-pv-drivers/HEAD/XenBootFix/XenBootFix.rc
--------------------------------------------------------------------------------
/installer/branding/Up.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcp-ng/win-pv-drivers/HEAD/installer/branding/Up.ico
--------------------------------------------------------------------------------
/installer/branding/New.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcp-ng/win-pv-drivers/HEAD/installer/branding/New.ico
--------------------------------------------------------------------------------
/installer/branding/info.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcp-ng/win-pv-drivers/HEAD/installer/branding/info.ico
--------------------------------------------------------------------------------
/installer/branding/xen.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcp-ng/win-pv-drivers/HEAD/installer/branding/xen.ico
--------------------------------------------------------------------------------
/installer/branding/DlgBmp.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcp-ng/win-pv-drivers/HEAD/installer/branding/DlgBmp.bmp
--------------------------------------------------------------------------------
/XenDriverUtils/NativeMethods.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://aka.ms/CsWin32.schema.json",
3 | "public": true
4 | }
5 |
--------------------------------------------------------------------------------
/installer/branding/BannrBmp.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcp-ng/win-pv-drivers/HEAD/installer/branding/BannrBmp.bmp
--------------------------------------------------------------------------------
/installer/branding/exclamic.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcp-ng/win-pv-drivers/HEAD/installer/branding/exclamic.ico
--------------------------------------------------------------------------------
/XenClean/UninstallDrivers.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using XenDriverUtils;
3 |
4 | namespace XenClean {
5 | internal static class UninstallDrivers {
6 | public static void Execute() {
7 | DriverUtils.UninstallDriverByNames(XenDeviceInfo.KnownDevices.Keys.ToArray());
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/XenClean/Logger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using XenDriverUtils;
3 |
4 | namespace XenClean {
5 | internal class ConsoleLogger : Logger {
6 | public ConsoleLogger() {
7 | }
8 |
9 | public override void Write(string message) {
10 | Console.WriteLine(message);
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/scripts/xen-guest-agent/build.rs:
--------------------------------------------------------------------------------
1 | #[cfg(windows)]
2 | extern crate winres;
3 |
4 | #[cfg(windows)]
5 | fn main() {
6 | let mut res = winres::WindowsResource::new();
7 | res.set_manifest_file("manifest.xml");
8 | include!("branding.rs");
9 | res.compile().unwrap();
10 | }
11 |
12 | #[cfg(unix)]
13 | fn main() {}
14 |
--------------------------------------------------------------------------------
/installer/Include.wxi:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/XenClean/UninstallRegistry.cs:
--------------------------------------------------------------------------------
1 | using XenDriverUtils;
2 |
3 | namespace XenClean {
4 | internal class UninstallRegistry {
5 | public static void Execute() {
6 | XenCleanup.ResetStartOverride();
7 |
8 | XenCleanup.XenfiltClassCleanup();
9 | XenCleanup.ResetUnplug();
10 | XenCleanup.ResetForceUnplug();
11 |
12 | XenCleanup.XenfiltReset();
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/XenBootFix/resource.h:
--------------------------------------------------------------------------------
1 | //{{NO_DEPENDENCIES}}
2 | // Microsoft Visual C++ generated include file.
3 | // Used by XenBootFix.rc
4 |
5 | // Next default values for new objects
6 | //
7 | #ifdef APSTUDIO_INVOKED
8 | #ifndef APSTUDIO_READONLY_SYMBOLS
9 | #define _APS_NEXT_RESOURCE_VALUE 101
10 | #define _APS_NEXT_COMMAND_VALUE 40001
11 | #define _APS_NEXT_CONTROL_VALUE 1001
12 | #define _APS_NEXT_SYMED_VALUE 101
13 | #endif
14 | #endif
15 |
--------------------------------------------------------------------------------
/installer/Folders.wxs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/installer/Tweaks.en-us.wxl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/XenBootFix/scripts/sign.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | param (
3 | [Parameter(Mandatory)]
4 | [string]$ProjectDir,
5 | [Parameter(Mandatory)]
6 | [string]$Configuration,
7 | [Parameter(Mandatory)]
8 | [string]$Platform
9 | )
10 |
11 | . "$ProjectDir\..\branding.ps1"
12 | . "$ProjectDir\..\scripts\branding-generic.ps1"
13 | . "$ProjectDir\..\scripts\sign.ps1"
14 |
15 | Set-SignerFileSignature -FilePath "$ProjectDir\$Platform\$Configuration\XenBootFix.exe"
16 |
--------------------------------------------------------------------------------
/installer/scripts/sign.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | param (
3 | [Parameter(Mandatory)]
4 | [string]$ProjectDir,
5 | [Parameter(Mandatory)]
6 | [string]$Configuration,
7 | [Parameter(Mandatory)]
8 | [string]$Platform
9 | )
10 |
11 | . "$ProjectDir\..\branding.ps1"
12 | . "$ProjectDir\..\scripts\branding-generic.ps1"
13 | . "$ProjectDir\..\scripts\sign.ps1"
14 |
15 | Set-SignerFileSignature -FilePath "$ProjectDir\bin\$Platform\$Configuration\*\*.msi"
16 |
--------------------------------------------------------------------------------
/DriverInstallCustomAction/scripts/sign.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | param (
3 | [Parameter(Mandatory)]
4 | [string]$ProjectDir,
5 | [Parameter(Mandatory)]
6 | [string]$Configuration,
7 | [Parameter(Mandatory)]
8 | [string]$Platform
9 | )
10 |
11 | . "$ProjectDir\..\branding.ps1"
12 | . "$ProjectDir\..\scripts\branding-generic.ps1"
13 | . "$ProjectDir\..\scripts\sign.ps1"
14 |
15 | Set-SignerFileSignature -FilePath "$ProjectDir\bin\$Platform\$Configuration\*\xeninst.CA.dll"
16 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "github-actions"
4 | directory: "/"
5 | schedule:
6 | interval: "weekly"
7 | cooldown:
8 | default-days: 15
9 | groups:
10 | trusted:
11 | patterns:
12 | - "actions/*"
13 | - package-ecosystem: "nuget"
14 | directory: "/"
15 | schedule:
16 | interval: "weekly"
17 | cooldown:
18 | default-days: 15
19 | ignore:
20 | - dependency-name: "WixToolset.*"
21 |
--------------------------------------------------------------------------------
/XenDriverUtils/Logger.cs:
--------------------------------------------------------------------------------
1 | namespace XenDriverUtils {
2 | public abstract class Logger {
3 | public abstract void Write(string message);
4 |
5 | private static Logger Instance = null;
6 |
7 | public static Logger SetLogger(Logger logger) {
8 | var old = Instance;
9 | Instance = logger;
10 | return old;
11 | }
12 |
13 | public static void Log(string message) {
14 | Instance?.Write(message);
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/installer/TimeProvider.en-us.wxl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/XenDriverUtils/scripts/sign.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | param (
3 | [Parameter(Mandatory)]
4 | [string]$ProjectDir,
5 | [Parameter(Mandatory)]
6 | [string]$Configuration,
7 | [Parameter(Mandatory)]
8 | [string]$Platform
9 | )
10 |
11 | . "$ProjectDir\..\branding.ps1"
12 | . "$ProjectDir\..\scripts\branding-generic.ps1"
13 | . "$ProjectDir\..\scripts\sign.ps1"
14 |
15 | Set-SignerFileSignature -FilePath `
16 | "$ProjectDir\bin\$Platform\$Configuration\*\xdutils.dll", `
17 | "$ProjectDir\bin\$Platform\$Configuration\*\Copy-XenVifSettings.ps1"
18 |
--------------------------------------------------------------------------------
/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/installer/Tools.en-us.wxl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/XenClean/Program.cs:
--------------------------------------------------------------------------------
1 | using XenClean;
2 | using XenDriverUtils;
3 |
4 | Logger.SetLogger(new ConsoleLogger());
5 | if (XenCleanup.IsSafeMode()) {
6 | Logger.Log("Skipping Xenvif offboarding in Safe Mode");
7 | } else {
8 | XenOffboard.BackupXenvif();
9 | XenOffboard.PrepareRestoreXenvif();
10 | }
11 | if (XenCleanup.IsSafeMode()) {
12 | Logger.Log("Skipping product uninstallation in Safe Mode");
13 | } else {
14 | UninstallProducts.Execute();
15 | }
16 | UninstallDevices.Execute();
17 | UninstallDrivers.Execute();
18 | UninstallServices.Execute(!XenCleanup.IsSafeMode());
19 | UninstallRegistry.Execute();
20 | Logger.Log("Finished, you must restart!");
21 |
--------------------------------------------------------------------------------
/installer/GuestAgent.en-us.wxl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/XenClean/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/installer/Drivers/Xenhid.wxs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/installer/Drivers/Xenvkbd.wxs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/XenClean/UninstallServices.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 | using Windows.Win32;
3 | using XenDriverUtils;
4 |
5 | namespace XenClean {
6 | internal static class UninstallServices {
7 | public static void Execute(bool removeNonDriverServices) {
8 | using var scm = PInvoke.OpenSCManager((string)null, (string)null, PInvoke.SC_MANAGER_ALL_ACCESS);
9 | if (scm.IsInvalid) {
10 | Logger.Log($"Cannot open SCM: error {Marshal.GetLastWin32Error()}");
11 | return;
12 | }
13 | foreach (var service in XenCleanup.DeleteableServices) {
14 | if (service.Item2 || removeNonDriverServices) {
15 | XenCleanup.DeleteService(scm, service.Item1);
16 | }
17 | }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/docs/offboard.md:
--------------------------------------------------------------------------------
1 | # Driver offboarding
2 |
3 | See the CA sequencing in [Driver.wxi](/installer/Driver.wxi).
4 |
5 | ## Xenbus and Xenvbd
6 |
7 | See [CleanupActions.cs](/DriverInstallCustomAction/CleanupActions.cs).
8 |
9 | ## Xenvif
10 |
11 | Each network adapter on Windows has a separate interface configuration bound to the interface GUID, which is itself bound to the device ID.
12 | While Xenvif contains functionalities to save emulated interface settings and apply them to the PV interface, there exists no functionality in the other direction.
13 | Thus the offboarding helper script [Copy-XenVifSettings.ps1](/XenDriverUtils/Copy-XenVifSettings.ps1).
14 |
15 | It consists of two components:
16 |
17 | - A preinstall task that backs up PV interface config;
18 | - A postreboot task that reapplies the backed up configs to the emulated interfaces.
19 |
--------------------------------------------------------------------------------
/XenDriverUtils/NativeMethods.txt:
--------------------------------------------------------------------------------
1 | INVALID_HANDLE_VALUE
2 | WIN32_ERROR
3 |
4 | DiInstallDriver
5 | DiUninstallDevice
6 |
7 | SetupUninstallOEMInf
8 | SUOI_FORCEDELETE
9 |
10 | SetupOpenInfFile
11 | SetupFindFirstLine
12 | SetupFindNextLine
13 | SetupGetLineText
14 | SetupGetStringField
15 | SetupCloseInfFile
16 |
17 | SetupDiGetClassDevs
18 | SetupDiEnumDeviceInfo
19 | SetupDiGetDeviceProperty
20 | SetupDiDestroyDeviceInfoList
21 |
22 | GUID_DEVCLASS_SYSTEM
23 | GUID_DEVCLASS_SCSIADAPTER
24 | GUID_DEVCLASS_NET
25 | GUID_DEVCLASS_HIDCLASS
26 | GUID_DEVCLASS_HDC
27 | DEVPROPTYPE
28 |
29 | GlobalAddAtom
30 | GlobalFindAtom
31 |
32 | RtlGetVersion
33 |
34 | CMP_WaitNoPendingInstallEvents
35 | WAIT_EVENT
36 |
37 | OpenSCManager
38 | OpenService
39 | ControlService
40 | DeleteService
41 | SC_MANAGER_ALL_ACCESS
42 | SERVICE_ALL_ACCESS
43 | SERVICE_CONTROL_STOP
44 |
--------------------------------------------------------------------------------
/installer/Drivers/Xenvif.wxs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/DriverInstallCustomAction/scripts/genfiles.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | param (
3 | [Parameter(Mandatory)]
4 | [string]$ProjectDir
5 | )
6 |
7 | . "$ProjectDir\..\branding.ps1"
8 | . "$ProjectDir\..\scripts\branding-generic.ps1"
9 |
10 | $BrandingFile = "$ProjectDir\Branding.cs"
11 | $OldBranding = Get-Content -Raw $BrandingFile -ErrorAction Ignore
12 | $NewBranding = `
13 | @"
14 | using System.Reflection;
15 | [assembly:AssemblyVersion("$(Get-PackageVersion Product)")]
16 | [assembly:AssemblyCompany("${Env:VENDOR_NAME}")]
17 | [assembly:AssemblyProduct("${Env:PRODUCT_NAME}")]
18 | [assembly:AssemblyCopyright("${Env:COPYRIGHT}")]
19 | [assembly:AssemblyTitle("Xen PV driver installation library")]
20 | "@
21 |
22 | if ($NewBranding -ne $OldBranding) {
23 | Write-Output "Updating Branding.cs"
24 | Set-Content -Path $BrandingFile -Value $NewBranding -NoNewline
25 | }
26 |
--------------------------------------------------------------------------------
/installer/Tools.wxs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/installer/Tweaks.wxs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
15 |
16 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/installer/Drivers/Xencons.wxs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/XenDriverUtils/VersionUtils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 | using Windows.Win32.System.SystemInformation;
4 |
5 | namespace XenDriverUtils {
6 | public static class VersionUtils {
7 | // Unlike Environment.OSVersion, RtlGetVersion won't lie to us regardless of host manifest.
8 | public static Version GetWindowsVersion() {
9 | var versionInfo = new OSVERSIONINFOW {
10 | dwOSVersionInfoSize = (uint)Marshal.SizeOf()
11 | };
12 | var ret = Windows.Wdk.PInvoke.RtlGetVersion(ref versionInfo);
13 | if (ret < 0) {
14 | throw new Exception($"RtlGetVersion failed with NTSTATUS {ret}");
15 | }
16 | return new Version(
17 | (int)versionInfo.dwMajorVersion,
18 | (int)versionInfo.dwMinorVersion,
19 | (int)versionInfo.dwBuildNumber);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/scripts/signer-ci.ps1:
--------------------------------------------------------------------------------
1 | # Prepare signer info from provided environment variables.
2 |
3 | #Requires -Version 7
4 |
5 | [CmdletBinding()]
6 | param (
7 | [Parameter(Mandatory)]
8 | [string]$OutFile
9 | )
10 |
11 | $ErrorActionPreference = 'Stop'
12 |
13 | $pfxBytes = [System.Convert]::FromBase64String([regex]::Replace($Env:SIGNER_PFX_BASE64, '[^a-zA-Z0-9+/=]', ''))
14 | $pfxPath = Join-Path $pwd "Signer.pfx"
15 | [IO.File]::WriteAllBytes($pfxPath, $pfxBytes)
16 | try {
17 | certutil -importpfx -f -user -p "" my $pfxPath nochain
18 | if ($LASTEXITCODE -ne 0) {
19 | throw "certutil failed with error $LASTEXITCODE"
20 | }
21 | $signer = (Get-PfxCertificate -FilePath $pfxPath -NoPromptForPassword).Thumbprint
22 | }
23 | finally {
24 | Remove-Item -Force $pfxPath
25 | }
26 | Add-Content -Path $OutFile -Value "`$Env:SIGNER = '$signer'" -Force
27 | Add-Content -Path $Env:GITHUB_STEP_SUMMARY -Value "Signer thumbprint: ``$signer``" -Force
28 |
--------------------------------------------------------------------------------
/testsign/install.ps1:
--------------------------------------------------------------------------------
1 | #Requires -RunAsAdministrator
2 |
3 | [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")]
4 | param (
5 | [Parameter()]
6 | [switch]$NoReboot
7 | )
8 |
9 | $ErrorActionPreference = "Stop"
10 |
11 | if (![Environment]::Is64BitProcess) {
12 | throw "Cannot install testsign certs from PowerShell x86!"
13 | }
14 |
15 | if (!$PSCmdlet.ShouldProcess("Local computer", "Set up testsigned XCP-ng driver - reduces your security")) {
16 | exit;
17 | }
18 |
19 | $System32 = [System.Environment]::SystemDirectory
20 |
21 | & "$System32\bcdedit.exe" -set testsigning on
22 | if ($LASTEXITCODE -ne 0) {
23 | throw "Cannot enable testsigning; is Secure Boot turned off?"
24 | }
25 |
26 | Get-ChildItem $PSScriptRoot\*.crt | ForEach-Object {
27 | & "$System32\certutil.exe" -addstore -f Root $_
28 | & "$System32\certutil.exe" -addstore -f TrustedPublisher $_
29 | }
30 |
31 | if (!$NoReboot) {
32 | Restart-Computer -Force
33 | }
34 |
--------------------------------------------------------------------------------
/docs/coexistence.md:
--------------------------------------------------------------------------------
1 | # Coexistence with other Xen PV drivers and tools
2 |
3 | Our driver package is not designed to coexist with other driver packages.
4 | The following checks are made to ensure a clean and reliable installation/uninstallation:
5 |
6 | - The installer's Upgrade table checks for other installed packages and denies installation if these are detected (see [Package.wxs](/installer/Package.wxs)). [XenClean](/XenClean/UninstallProducts.cs) removes any detected Xen PV driver packages.
7 | - The [CheckIncompatibleDevices and Check3PStorageDrivers](/DriverInstallCustomAction/ImmediateActions.cs) custom actions prevent installation if existing Xen drivers, Xen vendor device or third-party storage drivers are present.
8 | If you're creating your own installer package, edit [XenDeviceInfo.cs](/XenDriverUtils/XenDeviceInfo.cs) to add your own customized device IDs if necessary.
9 | - Uninstalling the Windows Guest Tools package will remove all existing Xen PV drivers and driver configuration.
10 |
--------------------------------------------------------------------------------
/installer/Branding.wxs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/installer/Drivers/Xeniface.wxs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/XenClean/scripts/sign.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | param (
3 | [Parameter(Mandatory)]
4 | [string]$ProjectDir,
5 | [Parameter(Mandatory)]
6 | [string]$Configuration,
7 | [Parameter(Mandatory)]
8 | [string]$Platform
9 | )
10 |
11 | . "$ProjectDir\..\branding.ps1"
12 | . "$ProjectDir\..\scripts\branding-generic.ps1"
13 | . "$ProjectDir\..\scripts\sign.ps1"
14 |
15 | Update-ScriptFileInfo `
16 | "$ProjectDir\bin\$Platform\$Configuration\*\Invoke-XenClean.ps1" `
17 | -Description "XenClean invocation script" `
18 | -Version "$(Get-PackageVersion XenClean)" `
19 | -Author $Env:VENDOR_NAME `
20 | -CompanyName $Env:VENDOR_NAME `
21 | -Copyright $Env:COPYRIGHT `
22 | -Force
23 |
24 | # re Copy-XenVifSettings: Copied deps are copied from source (not output) and therefore need signing again.
25 | Set-SignerFileSignature -FilePath `
26 | "$ProjectDir\bin\$Platform\$Configuration\*\XenClean.exe", `
27 | "$ProjectDir\bin\$Platform\$Configuration\*\Invoke-XenClean.ps1", `
28 | "$ProjectDir\bin\$Platform\$Configuration\*\Copy-XenVifSettings.ps1"
29 |
--------------------------------------------------------------------------------
/installer/UI/Detected3PStorageDriversDlg.wxs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/DriverInstallCustomAction/CustomActionUtils.cs:
--------------------------------------------------------------------------------
1 | using WixToolset.Dtf.WindowsInstaller;
2 | using Windows.Win32;
3 | using XenDriverUtils;
4 |
5 | namespace XenInstCA {
6 | internal static class CustomActionUtils {
7 | private static readonly string RebootScheduledAtomName = "WcaDeferredActionRequiresReboot";
8 |
9 | public static void ScheduleReboot() {
10 | // Use the same method as Util.wixext's CheckRebootRequired.
11 | // Ignore errors.
12 | Logger.Log("Scheduling reboot");
13 | _ = PInvoke.GlobalAddAtom(RebootScheduledAtomName);
14 | }
15 |
16 | public static bool IsRebootScheduled() {
17 | return PInvoke.GlobalFindAtom(RebootScheduledAtomName) != 0;
18 | }
19 |
20 | public static MessageResult ReportAction(Session session, string actionName, string message) {
21 | using var action = new Record(2);
22 | action[1] = actionName;
23 | action[2] = message;
24 | return session.Message(InstallMessage.ActionStart, action);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/scripts/Build-DriverSbom.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | param (
3 | [Parameter()]
4 | [string[]]$Drivers = @("xenbus", "xencons", "xenhid", "xeniface", "xennet", "xenvbd", "xenvif", "xenvkbd"),
5 | [Parameter(Mandatory)]
6 | [ValidateSet("Debug", "Release")]
7 | [string]$Configuration,
8 | [Parameter(Mandatory)]
9 | [ValidateSet("x86", "x64")]
10 | [string]$Platform
11 | )
12 |
13 | $ErrorActionPreference = "Stop"
14 |
15 | . $PSScriptRoot\..\branding.ps1
16 | . $PSScriptRoot\..\scripts\branding-generic.ps1
17 | . $PSScriptRoot\..\scripts\sign.ps1
18 |
19 | $OutputPath = "$PSScriptRoot\..\installer\output"
20 | foreach ($repo in $Drivers) {
21 | Push-Location $PSScriptRoot\..\$repo
22 | try {
23 | $DriverOutput = "$OutputPath\$Platform\$Configuration\$repo"
24 |
25 | sbom.exe generate -b $DriverOutput -bc . -D true -ps $Env:VENDOR_NAME -pn $repo -pv (Get-PackageVersion $repo)
26 | if ($LASTEXITCODE -ne 0) {
27 | throw "sbom-tool failed with error $LASTEXITCODE"
28 | }
29 | }
30 | finally {
31 | Pop-Location
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "xenbus"]
2 | path = xenbus
3 | url = https://github.com/xcp-ng/win-xenbus.git
4 | [submodule "xencons"]
5 | path = xencons
6 | url = https://github.com/xcp-ng/win-xencons.git
7 | [submodule "xenhid"]
8 | path = xenhid
9 | url = https://github.com/xcp-ng/win-xenhid.git
10 | [submodule "xeniface"]
11 | path = xeniface
12 | url = https://github.com/xcp-ng/win-xeniface.git
13 | [submodule "xennet"]
14 | path = xennet
15 | url = https://github.com/xcp-ng/win-xennet.git
16 | [submodule "xenvbd"]
17 | path = xenvbd
18 | url = https://github.com/xcp-ng/win-xenvbd.git
19 | [submodule "xenvif"]
20 | path = xenvif
21 | url = https://github.com/xcp-ng/win-xenvif.git
22 | [submodule "xenvkbd"]
23 | path = xenvkbd
24 | url = https://github.com/xcp-ng/win-xenvkbd.git
25 | [submodule "xen-guest-agent"]
26 | path = xen-guest-agent
27 | url = https://github.com/xcp-ng/xen-guest-agent.git
28 | [submodule "xenstore-win"]
29 | path = xenstore-win
30 | url = https://github.com/xcp-ng/xenstore-win.git
31 | [submodule "xentimeprovider"]
32 | path = xentimeprovider
33 | url = https://github.com/xcp-ng/win-xentimeprovider.git
34 |
--------------------------------------------------------------------------------
/.github/workflows/attest.yml:
--------------------------------------------------------------------------------
1 | name: Attest artifact
2 |
3 | on:
4 | workflow_call:
5 | inputs:
6 | subject-name:
7 | required: true
8 | type: string
9 | subject-digest:
10 | required: true
11 | type: string
12 | catalog-name:
13 | required: false
14 | type: string
15 |
16 | permissions:
17 | id-token: write
18 | contents: read
19 | attestations: write
20 |
21 | jobs:
22 | attest:
23 | runs-on: ubuntu-latest
24 | steps:
25 | - name: Attest artifact
26 | uses: actions/attest-build-provenance@v3
27 | with:
28 | subject-name: ${{inputs.subject-name}}
29 | subject-digest: ${{inputs.subject-digest}}
30 | - name: Download catalog
31 | if: ${{inputs.catalog-name != ''}}
32 | uses: actions/download-artifact@v6
33 | with:
34 | path: catalog/
35 | name: ${{inputs.catalog-name}}
36 | - name: Attest catalog
37 | if: ${{inputs.catalog-name != ''}}
38 | uses: actions/attest-build-provenance@v3
39 | with:
40 | subject-path: catalog/${{inputs.catalog-name}}.csv
41 |
--------------------------------------------------------------------------------
/XenClean/scripts/genfiles.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | param (
3 | [Parameter(Mandatory)]
4 | [string]$ProjectDir
5 | )
6 |
7 | . "$ProjectDir\..\branding.ps1"
8 | . "$ProjectDir\..\scripts\branding-generic.ps1"
9 |
10 | $BrandingFile = "$ProjectDir\Branding.cs"
11 | $OldBranding = Get-Content -Raw $BrandingFile -ErrorAction Ignore
12 | $NewBranding = `
13 | @"
14 | using System.Reflection;
15 | [assembly:AssemblyVersion("$(Get-PackageVersion XenClean)")]
16 | [assembly:AssemblyInformationalVersion("$(Get-PackageVersion Product)")]
17 | [assembly:AssemblyCompany("${Env:VENDOR_NAME}")]
18 | [assembly:AssemblyProduct("${Env:PRODUCT_NAME}")]
19 | [assembly:AssemblyCopyright("${Env:COPYRIGHT}")]
20 | [assembly:AssemblyTitle("Xen PV driver cleaning tool")]
21 |
22 | namespace XenClean {
23 | internal static class VersionInfo {
24 | public const string MsiUpgradeCodeX86 = "${Env:MSI_UPGRADE_CODE_X86}";
25 | public const string MsiUpgradeCodeX64 = "${Env:MSI_UPGRADE_CODE_X64}";
26 | }
27 | }
28 | "@
29 |
30 | if ($NewBranding -ne $OldBranding) {
31 | Write-Output "Updating Branding.cs"
32 | Set-Content -Path $BrandingFile -Value $NewBranding -NoNewline
33 | }
34 |
--------------------------------------------------------------------------------
/SBUpdate/SBUpdate.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Source Files
20 |
21 |
22 |
23 |
24 | Header Files
25 |
26 |
27 |
--------------------------------------------------------------------------------
/scripts/Compress-DriverCabinet.ps1:
--------------------------------------------------------------------------------
1 | param (
2 | [Parameter()]
3 | [string]$Path = "installer/output",
4 | [Parameter()]
5 | $OutPath = "output",
6 | [Parameter()]
7 | $OutName = "drivers.cab"
8 | )
9 |
10 | $ErrorActionPreference = "Stop"
11 |
12 | $OutPath = Convert-Path $OutPath
13 |
14 | $CabinetTemplate = @(
15 | ".OPTION EXPLICIT"
16 | ".Set DiskDirectoryTemplate=$OutPath"
17 | ".Set CabinetFileCountThreshold=0"
18 | ".Set FolderFileCountThreshold=0"
19 | ".Set FolderSizeThreshold=0"
20 | ".Set MaxCabinetSize=0"
21 | ".Set MaxDiskFileCount=0"
22 | ".Set MaxDiskSize=0"
23 | ".Set CompressionType=MSZIP"
24 | ".Set Cabinet=on"
25 | ".Set Compress=on"
26 | )
27 |
28 | Get-ChildItem -Directory $Path | ForEach-Object {
29 | $subdir = $_.Name
30 | $CabinetTemplate += @(".Set DestinationDir=$subdir")
31 | $_ | Get-ChildItem -Recurse -File | ForEach-Object {
32 | $CabinetTemplate += @($_.FullName)
33 | }
34 | }
35 |
36 | try {
37 | $TemplateFile = New-TemporaryFile
38 | Set-Content -Path $TemplateFile -Value $CabinetTemplate
39 |
40 | makecab.exe /F $TemplateFile /D CabinetNameTemplate=$OutName
41 | }
42 | finally {
43 | Remove-Item $TemplateFile
44 | Remove-Item setup.inf, setup.rpt
45 | }
46 |
--------------------------------------------------------------------------------
/XenDriverUtils/scripts/genfiles.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | param (
3 | [Parameter(Mandatory)]
4 | [string]$ProjectDir
5 | )
6 |
7 | . "$ProjectDir\..\branding.ps1"
8 | . "$ProjectDir\..\scripts\branding-generic.ps1"
9 |
10 | $BrandingFile = "$ProjectDir\Branding.cs"
11 | $OldBranding = Get-Content -Raw $BrandingFile -ErrorAction Ignore
12 | $NewBranding = `
13 | @"
14 | using System.Reflection;
15 | [assembly:AssemblyVersion("$(Get-PackageVersion Product)")]
16 | [assembly:AssemblyCompany("${Env:VENDOR_NAME}")]
17 | [assembly:AssemblyProduct("${Env:PRODUCT_NAME}")]
18 | [assembly:AssemblyCopyright("${Env:COPYRIGHT}")]
19 | [assembly:AssemblyTitle("Xen PV driver utility library")]
20 |
21 | namespace XenDriverUtils {
22 | internal static class VersionInfo {
23 | public const string VendorName = "${Env:VENDOR_NAME}";
24 | public const string ProductName = "${Env:PRODUCT_NAME}";
25 | public const string VendorPrefix = "${Env:VENDOR_PREFIX}";
26 | public const string VendorDeviceId = "${Env:VENDOR_DEVICE_ID}";
27 | public const string Copyright = "${Env:COPYRIGHT}";
28 | }
29 | }
30 | "@
31 |
32 | if ($NewBranding -ne $OldBranding) {
33 | Write-Output "Updating Branding.cs"
34 | Set-Content -Path $BrandingFile -Value $NewBranding -NoNewline
35 | }
36 |
--------------------------------------------------------------------------------
/DriverInstallCustomAction/MsiLogger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using WixToolset.Dtf.WindowsInstaller;
3 | using XenDriverUtils;
4 |
5 | namespace XenInstCA {
6 | internal class MsiSessionLogger : Logger {
7 | private readonly Session _session;
8 |
9 | public MsiSessionLogger(Session session) {
10 | _session = session;
11 | }
12 |
13 | public override void Write(string message) {
14 | _session.Log(message);
15 | }
16 | }
17 |
18 | internal class LoggerScope : IDisposable {
19 | private bool disposedValue;
20 | private Logger _old;
21 |
22 | public LoggerScope(Logger logger) {
23 | _old = Logger.SetLogger(logger);
24 | }
25 |
26 | protected virtual void Dispose(bool disposing) {
27 | if (!disposedValue) {
28 | if (disposing) {
29 | Logger.SetLogger(_old);
30 | _old = null;
31 | }
32 | disposedValue = true;
33 | }
34 | }
35 |
36 | public void Dispose() {
37 | // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
38 | Dispose(disposing: true);
39 | GC.SuppressFinalize(this);
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/installer/Drivers.en-us.wxl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/installer/scripts/genfiles.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | param (
3 | [Parameter(Mandatory)]
4 | [string]$ProjectDir
5 | )
6 |
7 | . "$ProjectDir\..\branding.ps1"
8 | . "$ProjectDir\..\scripts\branding-generic.ps1"
9 |
10 | $BrandingFile = "$ProjectDir\Branding.wxi"
11 | $OldBranding = Get-Content -Raw $BrandingFile -ErrorAction Ignore
12 | $NewBranding = @"
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | "@
21 |
22 | foreach ($package in @("Product", "Xenbus", "Xencons", "Xenhid", "Xeniface", "Xennet", "Xenvbd", "Xenvif", "Xenvkbd", "XenClean", "XenBootFix", "XenGuestAgent", "XenTimeProvider")) {
23 | $NewBranding += @"
24 |
25 |
26 | "@
27 | }
28 |
29 | $NewBranding += @"
30 |
31 |
32 |
33 |
34 | "@
35 |
36 | if ($NewBranding -ne $OldBranding) {
37 | Write-Output "Updating Branding.wxi"
38 | Set-Content -Path $BrandingFile -Value $NewBranding -NoNewline
39 | }
40 |
--------------------------------------------------------------------------------
/DriverInstallCustomAction/CustomAction.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/XenBootFix/XenBootFix.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Source Files
20 |
21 |
22 |
23 |
24 | Header Files
25 |
26 |
27 | Header Files
28 |
29 |
30 |
31 |
32 | Resource Files
33 |
34 |
35 |
--------------------------------------------------------------------------------
/scripts/sign.ps1:
--------------------------------------------------------------------------------
1 | $env:PSModulePath = [Environment]::GetEnvironmentVariable('PSModulePath', 'Machine')
2 | Import-Module PowerShellGet
3 | if (!(Get-PSDrive Cert)) {
4 | New-PSDrive -PSProvider Certificate -Name Cert -Root \
5 | }
6 |
7 | function Get-SignerObject() {
8 | if (!$Env:SIGNER) {
9 | return $null
10 | }
11 |
12 | if (${Env:SIGNER}.EndsWith(".pfx", [System.StringComparison]::OrdinalIgnoreCase)) {
13 | return Get-PfxCertificate -FilePath $Env:SIGNER
14 | }
15 | else {
16 | return Get-ChildItem Cert:\CurrentUser\My\$Env:SIGNER
17 | }
18 | }
19 |
20 | function Set-SignerFileSignature() {
21 | param (
22 | [Parameter()]
23 | [string[]]$FilePath
24 | )
25 |
26 | $SignerObject = Get-SignerObject
27 | if (!$SignerObject) {
28 | return
29 | }
30 |
31 | $signArgs = @{
32 | FilePath = $FilePath
33 | HashAlgorithm = "SHA256"
34 | Certificate = $SignerObject
35 | }
36 | if ($Env:TIMESTAMP_SERVER) {
37 | $signArgs["TimestampServer"] = $Env:TIMESTAMP_SERVER
38 | }
39 |
40 | Set-AuthenticodeSignature @signArgs
41 | }
42 |
43 | function Export-SignerCertificate {
44 | param (
45 | [Parameter()]
46 | [string[]]$OutDir
47 | )
48 |
49 | $SignerObject = Get-SignerObject
50 | if (!$SignerObject) {
51 | return
52 | }
53 |
54 | Export-Certificate -Cert $SignerObject -FilePath $OutDir\$($SignerObject.Thumbprint).crt -Type CERT -Force
55 | }
56 |
--------------------------------------------------------------------------------
/installer/Drivers/Xennet.wxs:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/scripts/Test-BuildAttestation.ps1:
--------------------------------------------------------------------------------
1 | #Requires -Version 7
2 |
3 | [CmdletBinding()]
4 | param (
5 | [Parameter(Mandatory)]
6 | [string]$Bundle,
7 | [Parameter(Mandatory)]
8 | [string]$Path,
9 | [Parameter(Mandatory)]
10 | [string]$Repository,
11 | [Parameter()]
12 | [string]$Filter,
13 | [Parameter()]
14 | [string[]]$Include,
15 | [Parameter()]
16 | [string[]]$Exclude
17 | )
18 |
19 | $ErrorActionPreference = 'Stop'
20 |
21 | $bundlePath = Convert-Path $Bundle
22 |
23 | # cache the trusted root for faster verification
24 | $root = New-TemporaryFile
25 | gh attestation trusted-root | Set-Content -Path $root -Encoding UTF8
26 | if ($LASTEXITCODE -ne 0) {
27 | throw "Cannot fetch trusted root"
28 | }
29 | Write-Information "Trusted root cached to $root"
30 |
31 | try {
32 | Push-Location $Path | Out-Null
33 | return (Get-ChildItem -Recurse -File -Filter $Filter -Include $Include -Exclude $Exclude |
34 | Resolve-Path -Relative |
35 | ForEach-Object {
36 | gh attestation verify $_ `
37 | --custom-trusted-root $root.FullName `
38 | --bundle $bundlePath `
39 | --deny-self-hosted-runners `
40 | --repo $Repository > $null
41 | if ($LASTEXITCODE -ne 0) {
42 | throw "Attestation failed for $_"
43 | }
44 | Write-Information "Attestation succeeded for $_"
45 | })
46 | }
47 | finally {
48 | Pop-Location | Out-Null
49 | Remove-Item $root -Force
50 | }
51 |
--------------------------------------------------------------------------------
/installer/TimeProvider.wxs:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # XCP-ng Windows Guest Tools
2 |
3 | This repository contains the XCP-ng Windows Guest Tools installer source code.
4 |
5 | The relevant source code may be found at these locations:
6 |
7 | * Drivers:
8 | * [Bus Device Driver](https://github.com/xcp-ng/win-xenbus)
9 | * [Interface Driver](https://github.com/xcp-ng/win-xeniface)
10 | * [Network Class Driver](https://github.com/xcp-ng/win-xenvif)
11 | * [Network Device Driver](https://github.com/xcp-ng/win-xennet)
12 | * [Storage Class Driver](https://github.com/xcp-ng/win-xenvbd)
13 | * [Console Driver](https://github.com/xcp-ng/win-xencons)
14 | * [Keyboard/Mouse Driver](https://github.com/xcp-ng/win-xenvkbd)
15 | * [HID Minidriver](https://github.com/xcp-ng/win-xenhid)
16 | * [Driver installation support library](XenDriverUtils/)
17 | * [Installer package](installer/)
18 | * [Custom WiX actions](DriverInstallCustomAction/)
19 | * [XenClean](XenClean/)
20 | * [XenBootFix](XenBootFix/)
21 | * [Xen Guest Agent](https://github.com/xcp-ng/xen-guest-agent)
22 | * [xenstore-win dependency](https://github.com/xcp-ng/xenstore-win)
23 | * [Xen Time Provider](https://github.com/xcp-ng/win-xentimeprovider)
24 | * [Developer support scripts](scripts/)
25 | * [Supplemental tools](extras/)
26 | * [Source documentation](docs/)
27 |
28 | # Usage
29 |
30 | Driver and tool projects are included as submodules of this repository:
31 |
32 | ```
33 | git clone --recursive https://github.com/xcp-ng/win-pv-drivers.git
34 | ```
35 |
36 | For further information, see the [source documentation](docs/).
37 |
--------------------------------------------------------------------------------
/installer/Drivers/Xenvbd.wxs:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/installer/Drivers.wxs:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/XenBootFix/scripts/genfiles.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | param (
3 | [Parameter(Mandatory)]
4 | [string]$ProjectDir
5 | )
6 |
7 | . "$ProjectDir\..\branding.ps1"
8 | . "$ProjectDir\..\scripts\branding-generic.ps1"
9 |
10 | $BrandingFile = "$ProjectDir\Branding.inc"
11 | $OldBranding = Get-Content -Raw $BrandingFile -ErrorAction Ignore
12 | $NewBranding = @"
13 | VS_VERSION_INFO VERSIONINFO
14 | FILEVERSION $((Get-PackageVersion XenBootFix).ToString().Replace(".", ","))
15 | PRODUCTVERSION $((Get-PackageVersion Product).ToString().Replace(".", ","))
16 | FILEFLAGSMASK 0x3fL
17 | #ifdef _DEBUG
18 | FILEFLAGS 0x1L
19 | #else
20 | FILEFLAGS 0x0L
21 | #endif
22 | FILEOS 0x40004L
23 | FILETYPE 0x1L
24 | FILESUBTYPE 0x0L
25 | BEGIN
26 | BLOCK "StringFileInfo"
27 | BEGIN
28 | BLOCK "200004b0"
29 | BEGIN
30 | VALUE "CompanyName", "${Env:VENDOR_NAME}"
31 | VALUE "FileDescription", "Xen PV boot fix tool"
32 | VALUE "FileVersion", "$(Get-PackageVersion XenBootFix)"
33 | VALUE "InternalName", "XenBootFix.exe"
34 | VALUE "LegalCopyright", "${Env:COPYRIGHT}"
35 | VALUE "OriginalFilename", "XenBootFix.exe"
36 | VALUE "ProductName", "${Env:PRODUCT_NAME}"
37 | VALUE "ProductVersion", "$(Get-PackageVersion Product)"
38 | END
39 | END
40 | BLOCK "VarFileInfo"
41 | BEGIN
42 | VALUE "Translation", 0x2000, 1200
43 | END
44 | END
45 |
46 | "@
47 |
48 | if ($NewBranding -ne $OldBranding) {
49 | Write-Output "Updating Branding.inc"
50 | Set-Content -Path $BrandingFile -Value $NewBranding -NoNewline
51 | }
52 |
--------------------------------------------------------------------------------
/XenClean/XenClean.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net462
6 | 9.0
7 | x64;x86;ARM64
8 | warnings
9 | app.manifest
10 | false
11 | false
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | PreserveNewest
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/SBUpdate/SBUpdate.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | // Windows headers don't provide definitions for these attributes, so use our own
16 | static constexpr DWORD SBUPDATE_VARIABLE_ATTRIBUTE_NON_VOLATILE = 0x00000001;
17 | static constexpr DWORD SBUPDATE_VARIABLE_ATTRIBUTE_BOOTSERVICE_ACCESS = 0x00000002;
18 | static constexpr DWORD SBUPDATE_VARIABLE_ATTRIBUTE_RUNTIME_ACCESS = 0x00000004;
19 | static constexpr DWORD SBUPDATE_VARIABLE_ATTRIBUTE_HARDWARE_ERROR_RECORD = 0x00000008;
20 | static constexpr DWORD SBUPDATE_VARIABLE_ATTRIBUTE_AUTHENTICATED_WRITE_ACCESS = 0x00000010;
21 | static constexpr DWORD SBUPDATE_VARIABLE_ATTRIBUTE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS = 0x00000020;
22 | static constexpr DWORD SBUPDATE_VARIABLE_ATTRIBUTE_APPEND_WRITE = 0x00000040;
23 |
24 | struct SecureBootVariable {
25 | const wchar_t *VariableName;
26 | const wchar_t *VariableGuid;
27 | const DWORD VariableAttributes;
28 | };
29 |
30 | // win32_encryptablevolume.mof
31 |
32 | static constexpr const wchar_t *FveNamespace = L"Root\\CIMV2\\Security\\MicrosoftVolumeEncryption";
33 | static constexpr const wchar_t *FveGetSecureBootBindingState = L"GetSecureBootBindingState";
34 | static constexpr const wchar_t *FveDisableKeyProtectors = L"DisableKeyProtectors";
35 |
36 | enum FveSecureBootBindingState {
37 | FveSecureBootBindingStateNotPossible,
38 | FveSecureBootBindingStateDisabledByPolicy,
39 | FveSecureBootBindingStatePossible,
40 | FveSecureBootBindingStateBound,
41 | };
42 |
--------------------------------------------------------------------------------
/XenBootFix/RegHive.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | class RegHive {
8 | public:
9 | RegHive(HKEY hkey, LPCWSTR subkey, LPCWSTR file) : _hkey(hkey), _subkey(subkey) {
10 | auto result = RegLoadKeyW(hkey, subkey, file);
11 | if (result != ERROR_SUCCESS)
12 | throw std::system_error(result, std::system_category(), "RegLoadKeyW");
13 | }
14 | RegHive(const RegHive &) = delete;
15 | RegHive &operator=(const RegHive &) = delete;
16 | RegHive(RegHive &&other) noexcept {
17 | swap(*this, other);
18 | }
19 | RegHive &operator=(RegHive &&other) noexcept {
20 | if (this != &other) {
21 | dispose();
22 | swap(*this, other);
23 | }
24 | return *this;
25 | };
26 | ~RegHive() {
27 | dispose();
28 | }
29 |
30 | constexpr HKEY HKey() const noexcept {
31 | return _hkey;
32 | }
33 |
34 | constexpr LPCWSTR SubKey() const noexcept {
35 | return _subkey;
36 | }
37 |
38 | int release() noexcept {
39 | dispose();
40 | }
41 |
42 | friend void swap(RegHive &self, RegHive &other) noexcept {
43 | using std::swap;
44 | swap(self._hkey, other._hkey);
45 | swap(self._subkey, other._subkey);
46 | }
47 |
48 | private:
49 | HKEY _hkey = (HKEY)INVALID_HANDLE_VALUE;
50 | LPCWSTR _subkey = nullptr;
51 |
52 | void dispose() noexcept {
53 | if (_hkey != (HKEY)INVALID_HANDLE_VALUE) {
54 | auto result = RegUnLoadKeyW(_hkey, _subkey);
55 | if (result != ERROR_SUCCESS)
56 | wprintf(L"Cannot unload key: 0x%lx\n", result);
57 | _hkey = (HKEY)INVALID_HANDLE_VALUE;
58 | _subkey = nullptr;
59 | }
60 | }
61 | };
62 |
--------------------------------------------------------------------------------
/XenDriverUtils/XenDriverUtils.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net462
5 | xdutils
6 | 9.0
7 | x64;x86;ARM64
8 | warnings
9 | false
10 | false
11 |
12 |
13 |
14 |
15 | all
16 | runtime; build; native; contentfiles; analyzers; buildtransitive
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | PreserveNewest
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/DriverInstallCustomAction/DriverInstallCustomAction.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net462
5 | xeninst
6 | XenInstCA
7 | 9.0
8 | x64;x86;ARM64
9 | warnings
10 | false
11 | false
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 |
--------------------------------------------------------------------------------
/scripts/Get-DriverSubmodule.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | param (
3 | [Switch]$Downstream
4 | )
5 |
6 | $submodules = "xenbus", "xencons", "xenhid", "xeniface", "xennet", "xenvbd", "xenvif", "xenvkbd"
7 |
8 | foreach ($submodule in $submodules) {
9 | # Get submodule head from parent
10 | $submodule_head_line = git ls-tree HEAD $submodule
11 | $submodule_head = ($submodule_head_line.Trim() -split '\s+')[2]
12 |
13 | # Get current branch
14 | $branch = (git -C $submodule rev-parse --abbrev-ref HEAD)
15 |
16 | # Compare with parent repo's commit
17 | $commits_since_parent = git -C $submodule log --oneline "$submodule_head..HEAD"
18 |
19 | # Compare with master
20 | $downstream_commits = $null
21 | if ($Downstream) {
22 | # Use git cherry to find commits on the branch that are not on master.
23 | # Equivalent commits (by patch-id) are marked with '-'
24 | # Unique commits are marked with '+'
25 | $cherry_output = git -C $submodule cherry master $branch
26 | $downstream_commits = foreach ($line in $cherry_output) {
27 | if (-not($line)) {
28 | continue
29 | }
30 | $sign, $sha = $line.Split(' ', 2)
31 | $oneline = git -C $submodule log -1 --pretty="format:%h %s" $sha
32 | "$sign $oneline"
33 | }
34 | }
35 |
36 | if (($commits_since_parent) -or ($downstream_commits)) {
37 | Write-Output "--- Submodule: $submodule ---"
38 | Write-Output "Current branch: $branch"
39 | Write-Output ""
40 |
41 | if (!$Downstream -and $commits_since_parent) {
42 | Write-Output "Uncommitted changes from ${submodule_head}:"
43 | $commits_since_parent
44 | Write-Output ""
45 | }
46 |
47 | if ($Downstream -and $downstream_commits) {
48 | Write-Output "Downstream changes in ${branch}:"
49 | $downstream_commits
50 | Write-Output ""
51 | }
52 |
53 | Write-Output ""
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/scripts/Compare-DriverSource.ps1:
--------------------------------------------------------------------------------
1 | # Script to compare driver include/ and src/common/ directories against xenbus
2 |
3 | param (
4 | [Parameter()]
5 | [string[]]$Drivers = @("xencons", "xenhid", "xeniface", "xennet", "xenvbd", "xenvif", "xenvkbd"),
6 | [Parameter()]
7 | $ReferenceDriver = "xenbus",
8 | [Parameter()]
9 | $SubDirs = @("include", "src/common"),
10 | [Parameter()]
11 | [switch]$ShowDiff
12 | )
13 |
14 | foreach ($driver in $Drivers) {
15 | $hasDifference = $false
16 | Write-Host "--- Comparing $driver against $ReferenceDriver ---"
17 |
18 | foreach ($subDir in $SubDirs) {
19 | $referencePath = Join-Path -Path $ReferenceDriver -ChildPath $subDir
20 | $targetPath = Join-Path -Path $driver -ChildPath $subDir
21 |
22 | if (-not (Test-Path -Path $referencePath -PathType Container)) { continue }
23 | if (-not (Test-Path -Path $targetPath -PathType Container)) { continue }
24 |
25 | $referenceFiles = Get-ChildItem -Path $referencePath -File
26 | if ($null -eq $referenceFiles) {
27 | continue
28 | }
29 |
30 | foreach ($refFile in $referenceFiles) {
31 | $targetFile = Join-Path -Path $targetPath -ChildPath $refFile.Name
32 |
33 | if (Test-Path -Path $targetFile -PathType Leaf) {
34 | $diffOutput = git diff --no-index --quiet --exit-code $refFile.FullName $targetFile 2>&1
35 | if ($LASTEXITCODE -ne 0) {
36 | $hasDifference = $true
37 | Write-Host "[$driver] MISMATCH in '$($refFile.FullName)' vs '$targetFile'"
38 |
39 | if ($ShowDiff) {
40 | $diffOutput = git diff --no-index $refFile.FullName $targetFile 2>&1
41 | # Print the diff output
42 | Write-Host ($diffOutput | Out-String)
43 | }
44 | }
45 | }
46 | }
47 | }
48 |
49 | if (-not $hasDifference) {
50 | Write-Host "[$driver] OK: No content differences found in common files."
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/installer/XenDrivers.wixproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | x86;x64;ARM64
4 | XenTools-$(Platform)
5 | 1103
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | branding
27 |
28 |
29 | output
30 |
31 |
32 | xenguestagent
33 |
34 |
35 | xentimeprovider
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/XenDriverUtils/XenOffboard.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Reflection;
5 |
6 | namespace XenDriverUtils {
7 | public class XenOffboard {
8 | enum ScriptMode {
9 | Backup,
10 | Restore,
11 | }
12 |
13 | enum ExecutionMode {
14 | Install,
15 | Invoke,
16 | }
17 |
18 | enum DeviceType {
19 | Paravirtualized,
20 | Emulated,
21 | }
22 |
23 | static void RunCopyXenvifScript(ScriptMode mode, ExecutionMode execMode, DeviceType deviceType) {
24 | var dllPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
25 | var scriptPath = Path.Combine(dllPath, "Copy-XenVifSettings.ps1");
26 | var powershellPath = Path.Combine(Environment.SystemDirectory, "WindowsPowerShell\\v1.0\\powershell.exe");
27 |
28 | var startInfo = new ProcessStartInfo() {
29 | FileName = powershellPath,
30 | Arguments = $"-ExecutionPolicy Bypass -File \"{scriptPath}\" -{mode} -{execMode} -{deviceType}",
31 | UseShellExecute = false,
32 | RedirectStandardOutput = true,
33 | RedirectStandardError = true,
34 | CreateNoWindow = true
35 | };
36 |
37 | using var process = Process.Start(startInfo);
38 | process.WaitForExit();
39 |
40 | var output = process.StandardOutput.ReadToEnd();
41 | var error = process.StandardError.ReadToEnd();
42 |
43 | if (process.ExitCode != 0) {
44 | Logger.Log($"Copy-XenVifSettings.ps1 error {process.ExitCode}: {error}");
45 | throw new Exception($"Copy-XenVifSettings.ps1 error {process.ExitCode}");
46 | }
47 |
48 | Logger.Log($"Copy-XenVifSettings.ps1 output: {output}");
49 | }
50 |
51 | public static void BackupXenvif() {
52 | Logger.Log($"Backing up Xenvif settings");
53 | RunCopyXenvifScript(ScriptMode.Backup, ExecutionMode.Invoke, DeviceType.Paravirtualized);
54 | }
55 |
56 | public static void PrepareRestoreXenvif() {
57 | Logger.Log($"Scheduling Xenvif restore");
58 | RunCopyXenvifScript(ScriptMode.Restore, ExecutionMode.Install, DeviceType.Emulated);
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/XenClean/UninstallDevices.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Windows.Win32;
5 | using Windows.Win32.Foundation;
6 | using Windows.Win32.Devices.DeviceAndDriverInstallation;
7 | using XenDriverUtils;
8 |
9 | namespace XenClean {
10 | internal static class UninstallDevices {
11 | static readonly List UninstallOrder = new() {
12 | "Xenvbd",
13 | "Xennet",
14 | "Xenvif",
15 | "Xenhid",
16 | "Xenvkbd",
17 | "Xencons",
18 | "Xeniface",
19 | "Xendevice",
20 | "Xenclass",
21 | "Xenbus",
22 | };
23 |
24 | static void RemoveDevices(XenDeviceInfo xenInfo) {
25 | var devInfo = PInvoke.SetupDiGetClassDevs(
26 | xenInfo.ClassGuid,
27 | null,
28 | HWND.Null,
29 | xenInfo.ClassGuid.HasValue ? 0 : SETUP_DI_GET_CLASS_DEVS_FLAGS.DIGCF_ALLCLASSES);
30 | bool needsReboot = false;
31 |
32 | foreach (var devInfoData in DriverUtils.EnumerateDevices(devInfo)) {
33 | List compatibleIds = DriverUtils.GetDeviceHardwareAndCompatibleIds(devInfo, devInfoData);
34 | if (compatibleIds.All(x => !xenInfo.MatchesId(x, checkKnown: true, checkIncompatible: true))) {
35 | continue;
36 | }
37 |
38 | var instanceId = DriverUtils.GetDeviceInstanceId(devInfo, devInfoData);
39 | if (instanceId != null) {
40 | Logger.Log($"Found device: {instanceId}");
41 | } else {
42 | Logger.Log($"Found device");
43 | }
44 |
45 | try {
46 | DriverUtils.UninstallDevice(devInfo, devInfoData, out var thisNeedsReboot);
47 | needsReboot |= thisNeedsReboot;
48 | } catch (Exception ex) {
49 | Logger.Log($"Cannot uninstall device: {ex.Message}");
50 | }
51 | }
52 | }
53 |
54 | public static void Execute() {
55 | foreach (var driver in UninstallOrder) {
56 | var xenInfo = XenDeviceInfo.KnownDevices[driver];
57 | Logger.Log($"Uninstalling {driver}");
58 | RemoveDevices(xenInfo);
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/installer/Package.en-us.wxl:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
19 |
20 |
21 |
22 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/installer/InstCA.wxs:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/installer/GuestAgent.wxs:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
14 |
15 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
40 |
45 |
46 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/installer/Drivers/Xenbus.wxs:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/XenClean/Invoke-XenClean.ps1:
--------------------------------------------------------------------------------
1 |
2 | <#PSScriptInfo
3 |
4 | .VERSION 1.0
5 |
6 | .GUID 5948fb9f-c421-4e12-82db-383de1663dca
7 |
8 | .AUTHOR Xen Project
9 |
10 | .COMPANYNAME
11 |
12 | .COPYRIGHT
13 |
14 | .TAGS
15 |
16 | .LICENSEURI
17 |
18 | .PROJECTURI
19 |
20 | .ICONURI
21 |
22 | .EXTERNALMODULEDEPENDENCIES
23 |
24 | .REQUIREDSCRIPTS
25 |
26 | .EXTERNALSCRIPTDEPENDENCIES
27 |
28 | .RELEASENOTES
29 |
30 |
31 | #>
32 |
33 | <#
34 |
35 | .DESCRIPTION
36 | XenClean invocation script
37 |
38 | #>
39 |
40 | #Requires -RunAsAdministrator
41 |
42 | [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")]
43 | param (
44 | [Parameter()]
45 | [switch]$NoReboot
46 | )
47 |
48 | $ErrorActionPreference = "Stop"
49 |
50 | if (![Environment]::Is64BitProcess) {
51 | throw "XenClean cannot run in PowerShell x86!"
52 | }
53 |
54 | function Export-XenSettings {
55 | param($LogPath, $Suffix)
56 |
57 | Write-Host "Saving settings$Suffix..."
58 |
59 | & "$System32\pnputil.exe" /enum-drivers > "$LogPath/drivers$Suffix.log"
60 | if ([System.Environment]::OSVersion.Version -ge [version]::Parse("10.0.18362")) {
61 | & "$System32\pnputil.exe" /enum-devices /relations > "$LogPath/devices$Suffix.log"
62 | }
63 |
64 | & "$System32\reg.exe" export "HKLM\SYSTEM\CurrentControlSet\Control\Class\{4d36e96a-e325-11ce-bfc1-08002be10318}" "$LogPath/hdc$Suffix.reg" /y >> "$LogPath/export$Suffix.log"
65 | & "$System32\reg.exe" export "HKLM\SYSTEM\CurrentControlSet\Control\Class\{4d36e97d-e325-11ce-bfc1-08002be10318}" "$LogPath/system$Suffix.reg" /y >> "$LogPath/export$Suffix.log"
66 | foreach ($service in @("XEN", "xenbus", "xencons", "xenfilt", "xenhid", "xeniface", "xennet", "xenvbd", "xenvif", "xenvkbd", "Tcpip", "Tcpip6")) {
67 | & "$System32\reg.exe" export "HKLM\SYSTEM\CurrentControlSet\Services\$Service" "$LogPath/service-$Service$Suffix.reg" /y >> "$LogPath/export$Suffix.log"
68 | }
69 | & "$System32\reg.exe" export "HKLM\SOFTWARE\XenOffboard" "$LogPath/xenoffboard$Suffix.reg" /y >> "$LogPath/export$Suffix.log"
70 | }
71 |
72 | if (!$PSCmdlet.ShouldProcess("Local computer", "Remove Xen drivers and tools")) {
73 | exit;
74 | }
75 |
76 | $System32 = [System.Environment]::SystemDirectory
77 | $DirName = "xenclean-$(Get-Date -Format FileDateTime)"
78 | $LogPath = "$env:TEMP\$DirName"
79 | New-Item -ItemType Directory -Path $LogPath -Force
80 |
81 | Export-XenSettings -LogPath $LogPath -Suffix "-pre"
82 | Write-Host "Running XenClean, be patient..."
83 | & "$PSScriptRoot\bin\XenClean.exe" > "$LogPath/xenclean.log"
84 | Export-XenSettings -LogPath $LogPath -Suffix "-post"
85 |
86 | Copy-Item -Recurse -ErrorAction SilentlyContinue $LogPath $Env:SystemDrive\$DirName
87 |
88 | if (!$NoReboot) {
89 | Restart-Computer -Force
90 | }
91 |
--------------------------------------------------------------------------------
/scripts/Test-DriverInf.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | param (
3 | [Parameter()]
4 | [string[]]$Drivers = @("xenbus", "xencons", "xenhid", "xeniface", "xennet", "xenvbd", "xenvif", "xenvkbd"),
5 | [Parameter(Mandatory)]
6 | [ValidateSet("Debug", "Release")]
7 | [string]$Configuration,
8 | [Parameter(Mandatory)]
9 | [ValidateSet("x86", "x64")]
10 | [string]$Platform,
11 | [Parameter()]
12 | [string]$OutputPath = "$PSScriptRoot\..\installer\output",
13 | [Parameter()]
14 | [string]$DriverRequirementsBuild,
15 | [Parameter()]
16 | [string]$RuleVersion,
17 | [Parameter()]
18 | [ValidateSet("", "WHQL", "Universal", "Windows")]
19 | [string]$PrintErrors,
20 | [Parameter()]
21 | [switch]$Detailed
22 | )
23 |
24 | $ErrorActionPreference = 'Stop'
25 |
26 | $result = @{}
27 | $output = @{}
28 |
29 | $PrintErrorsMap = @{
30 | WHQL = "/h"
31 | Universal = "/u"
32 | Windows = "/w"
33 | }
34 |
35 | foreach ($driver in $Drivers) {
36 | foreach ($mode in @("/h", "/u", "/w")) {
37 | $inf = "$OutputPath\$Platform\$Configuration\$driver\$driver.inf"
38 |
39 | if ($null -eq $result[$driver]) {
40 | $result[$driver] = @{}
41 | }
42 | if ($null -eq $output[$driver]) {
43 | $output[$driver] = @{}
44 | }
45 |
46 | if (!(Test-Path $inf)) {
47 | throw "Inf '$inf' doesn't exist"
48 | }
49 |
50 | $params = @()
51 | if ($DriverRequirementsBuild) {
52 | $params += @("/wbuild", $DriverRequirementsBuild)
53 | }
54 | if ($RuleVersion) {
55 | $params += @("/rulever", $RuleVersion)
56 | }
57 | if ($Detailed) {
58 | $params += @("/v")
59 | }
60 |
61 | $output[$driver][$mode] = (& "${Env:WindowsSdkDir}Tools\${Env:WindowsSDKLibVersion}\$Platform\infverif.exe" $mode $inf @params)
62 | $result[$driver][$mode] = $LASTEXITCODE
63 | }
64 | }
65 |
66 | $dnml = ($Drivers | Select-Object -ExpandProperty Length | Measure-Object -Maximum).Maximum
67 | Write-Host (" " * ($dnml + 1) + "WHQL Univ Wind")
68 | foreach ($driver in $Drivers) {
69 | $line = $driver.PadRight($dnml) + " "
70 | foreach ($mode in @("/h", "/u", "/w")) {
71 | if ($result[$driver][$mode] -eq 0) {
72 | $line += ".... "
73 | }
74 | else {
75 | $line += "Fail "
76 | }
77 | }
78 | Write-Host $line
79 | }
80 | if ($PrintErrors) {
81 | Write-Host
82 | $mode = $PrintErrorsMap[$PrintErrors]
83 | foreach ($driver in $Drivers) {
84 | if ($result[$driver][$mode] -ne 0) {
85 | Write-Host "InfVerif failures for ${driver}:"
86 | $output[$driver][$mode] | Write-Host
87 | Write-Host
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/DriverInstallCustomAction/CleanupActions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Windows.Win32;
3 | using WixToolset.Dtf.WindowsInstaller;
4 | using XenDriverUtils;
5 |
6 | namespace XenInstCA {
7 | public class CleanupActions {
8 | [CustomAction]
9 | public static ActionResult XenbusCleanup(Session session) {
10 | using var logScope = new LoggerScope(new MsiSessionLogger(session));
11 | XenCleanup.XenfiltClassCleanup();
12 | XenCleanup.ResetUnplug();
13 | XenCleanup.ResetForceUnplug();
14 | return ActionResult.Success;
15 | }
16 |
17 | [CustomAction]
18 | public static ActionResult XenvbdCleanup(Session session) {
19 | using var logScope = new LoggerScope(new MsiSessionLogger(session));
20 | XenCleanup.ResetStartOverride();
21 | return ActionResult.Success;
22 | }
23 |
24 | [CustomAction]
25 | public static ActionResult XenfiltReset(Session session) {
26 | using var logScope = new LoggerScope(new MsiSessionLogger(session));
27 | XenCleanup.XenfiltReset();
28 | return ActionResult.Success;
29 | }
30 |
31 | [CustomAction]
32 | public static ActionResult XenServiceDelete(Session session) {
33 | using var logScope = new LoggerScope(new MsiSessionLogger(session));
34 |
35 | if (!session.CustomActionData.TryGetValue("Services", out var services))
36 | return ActionResult.Success;
37 | var serviceList = services.Split(',');
38 |
39 | using var scm = PInvoke.OpenSCManager((string)null, (string)null, PInvoke.SC_MANAGER_ALL_ACCESS);
40 | foreach (var serviceName in serviceList) {
41 | XenCleanup.DeleteService(scm, serviceName);
42 | }
43 |
44 | return ActionResult.Success;
45 | }
46 |
47 | [CustomAction]
48 | public static ActionResult XenvifBackup(Session session) {
49 | using var logScope = new LoggerScope(new MsiSessionLogger(session));
50 | if (XenCleanup.IsSafeMode()) {
51 | Logger.Log("Skipping Xenvif backup in Safe Mode");
52 | } else {
53 | XenOffboard.BackupXenvif();
54 | }
55 | return ActionResult.Success;
56 | }
57 |
58 | [CustomAction]
59 | public static ActionResult XenvifPrepareRestore(Session session) {
60 | using var logScope = new LoggerScope(new MsiSessionLogger(session));
61 | if (XenCleanup.IsSafeMode()) {
62 | Logger.Log("Skipping Xenvif restore in Safe Mode");
63 | } else {
64 | XenOffboard.PrepareRestoreXenvif();
65 | }
66 | return ActionResult.Success;
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/XenClean/UninstallProducts.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Management;
6 | using XenDriverUtils;
7 |
8 | namespace XenClean {
9 | internal static class UninstallProducts {
10 | static readonly List KnownUpgradeCodes = new() {
11 | // x64 drivers version 6.6.557 from XS 6.6.90
12 | // must be uninstalled in the correct order
13 | // Citrix XenServer Tools Installer
14 | "{21EF141F-9126-42DA-93CD-B50442047420}",
15 | // Citrix XenServer Windows Guest Agent
16 | "{48E5492C-6843-452E-97A2-A5FE2D24B141}",
17 | // Citrix XenServer VSS Provider
18 | "{D8709720-65B7-4CD9-9F51-68DB592B604D}",
19 | // Citrix Xen Windows x64 PV Drivers
20 | "{53858014-F814-49A1-9D63-CA2578432E73}",
21 |
22 | // Citrix uses the same package code as the Citrix XenServer Windows Guest Agent
23 | // in their multi-package XS 6.6 drivers for their 7.1 series drivers
24 | // (aka. 48E5492C-6843-452E-97A2-A5FE2D24B141)
25 | // XCP-ng 8.2 x64 also uses the same upgrade code.
26 |
27 | // Citrix Hypervisor/XS8
28 | "{AF9B2559-3E91-4206-98C2-F560009FF7F1}",
29 |
30 | // generic x86 (does not work due to check in Invoke-XenClean)
31 | "{10828840-D8A9-4953-B44A-1F1D3CD7ECB0}",
32 | // generic x64
33 | "{D60FED1E-316C-41B0-B7A5-E44951A82618}",
34 |
35 | // ours
36 | VersionInfo.MsiUpgradeCodeX86,
37 | VersionInfo.MsiUpgradeCodeX64,
38 | };
39 |
40 | public static void Execute() {
41 | foreach (var upgradeCode in KnownUpgradeCodes) {
42 | Logger.Log($"Trying to uninstall products with upgrade code {upgradeCode}");
43 | var moSearcher = new ManagementObjectSearcher(
44 | $"SELECT ProductCode FROM Win32_Property WHERE Property='UpgradeCode' AND Value='{upgradeCode}'");
45 | var moObjects = moSearcher.Get();
46 | var msiexecPath = Path.Combine(Environment.SystemDirectory, "msiexec.exe");
47 | foreach (var moObject in moObjects) {
48 | Logger.Log($"Uninstalling product {moObject["ProductCode"]}");
49 | using var msiexecProcess = Process.Start(
50 | msiexecPath,
51 | $"/x \"{moObject["ProductCode"]}\" /passive /norestart");
52 | msiexecProcess.WaitForExit();
53 | if (msiexecProcess.ExitCode != 0) {
54 | Logger.Log($"Msiexec exited with code {msiexecProcess.ExitCode}");
55 | }
56 | }
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/installer/Driver.wxi:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/scripts/xen-guest-agent/genfiles.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | param (
3 | [Parameter(Mandatory)]
4 | [string]$ProjectDir
5 | )
6 |
7 | . "$ProjectDir\..\branding.ps1"
8 | . "$ProjectDir\..\scripts\branding-generic.ps1"
9 |
10 | function Update-BrandingFile {
11 | $BrandingFile = "$ProjectDir\xen-guest-agent\branding.rs"
12 | $OldBranding = Get-Content -Raw $BrandingFile -ErrorAction Ignore
13 |
14 | $FileVersion = Get-PackageVersion XenGuestAgent
15 | $NewFileVersionValue = [string]::Format("0x{0:x}", `
16 | ([uint64]$FileVersion.Major -shl 48) -bor `
17 | ([uint64]$FileVersion.Minor -shl 32) -bor `
18 | ([uint64]$FileVersion.Build -shl 16) -bor `
19 | [uint64]$FileVersion.Revision)
20 |
21 | $ProductVersion = Get-PackageVersion Product
22 | $NewProductVersionValue = [string]::Format("0x{0:x}", `
23 | ([uint64]$ProductVersion.Major -shl 48) -bor `
24 | ([uint64]$ProductVersion.Minor -shl 32) -bor `
25 | ([uint64]$ProductVersion.Build -shl 16) -bor `
26 | [uint64]$ProductVersion.Revision)
27 |
28 | $NewBranding = @"
29 | loop {
30 | res.set_version_info(winres::VersionInfo::FILEVERSION, ${NewFileVersionValue});
31 | res.set_version_info(winres::VersionInfo::PRODUCTVERSION, ${NewProductVersionValue});
32 |
33 | let crate_version = env!("CARGO_PKG_VERSION");
34 | let file_description = format!("Xen Guest Agent (v{crate_version}, Rust-based)");
35 |
36 | res.set("CompanyName", "${Env:VENDOR_NAME}");
37 | res.set("FileDescription", &file_description);
38 | res.set("FileVersion", "$($FileVersion.ToString())");
39 | res.set("InternalName", "xen-guest-agent.exe");
40 | res.set("LegalCopyright", "${Env:COPYRIGHT}");
41 | res.set("OriginalFilename", "xen-guest-agent.exe");
42 | res.set("ProductName", "${Env:PRODUCT_NAME}");
43 | res.set("ProductVersion", "$($ProductVersion.ToString())");
44 |
45 | break;
46 | }
47 | "@
48 |
49 | if ($NewBranding -ne $OldBranding) {
50 | Write-Output "Updating branding.rs"
51 | Set-Content -Path $BrandingFile -Value $NewBranding -NoNewline
52 | }
53 | }
54 |
55 | function Update-AgentVersionFile {
56 | $VersionFile = "$ProjectDir\publishers\publisher-xenstore\src\version.rs"
57 | $OldVersion = Get-Content -Raw $VersionFile -ErrorAction Ignore
58 |
59 | $Version = Get-PackageVersion Product
60 |
61 | $NewVersion = @"
62 | pub(crate) const AGENT_VERSION_MAJOR: &str = "$($Version.Major)"; // XO does not show version at all if 0
63 | pub(crate) const AGENT_VERSION_MINOR: &str = "$($Version.Minor)";
64 | pub(crate) const AGENT_VERSION_MICRO: &str = "$($Version.Build)"; // XAPI exposes "-1" if missing
65 | pub(crate) const AGENT_VERSION_BUILD: &str = "$($Version.Revision)";
66 | "@
67 |
68 | if ($NewVersion -ne $OldVersion) {
69 | Write-Output "Updating $VersionFile"
70 | Set-Content -Path $VersionFile -Value $NewVersion -NoNewline
71 | }
72 | }
73 |
74 | Update-BrandingFile
75 | Update-AgentVersionFile
76 |
--------------------------------------------------------------------------------
/scripts/branding-generic.ps1:
--------------------------------------------------------------------------------
1 | function Get-PackageVersion {
2 | [CmdletBinding()]
3 | param (
4 | [Parameter(Position = 1)]
5 | [string]$PackageName = 'Product',
6 | # Don't fill in 0 for non-existent build/revision numbers.
7 | [Parameter()]
8 | [switch]$Raw
9 | )
10 | $VersionString = $PackageVersions[$PackageName]
11 | if (!$VersionString) {
12 | throw "Cannot get $PackageName version"
13 | }
14 | $RawVersion = [version]::Parse($VersionString)
15 | if ($Raw) {
16 | return $RawVersion
17 | }
18 | else {
19 | $build = if ($RawVersion.Build -eq -1) { 0 } else { $RawVersion.Build }
20 | $rev = if ($RawVersion.Revision -eq -1) { 0 } else { $RawVersion.Revision }
21 | return [version]::new($RawVersion.Major, $RawVersion.Minor, $build, $rev)
22 | }
23 | }
24 |
25 | if (!$Env:VENDOR_NAME) {
26 | $Env:VENDOR_NAME = 'Xen Project'
27 | }
28 |
29 | if (!$Env:VENDOR_PREFIX) {
30 | $Env:VENDOR_PREFIX = 'XP'
31 | }
32 |
33 | if (!$Env:PRODUCT_NAME) {
34 | $Env:PRODUCT_NAME = 'Xen'
35 | }
36 |
37 | # xeniface
38 | if (!$Env:OBJECT_PREFIX) {
39 | $Env:OBJECT_PREFIX = 'XenProject'
40 | }
41 |
42 | if (!$Env:COPYRIGHT) {
43 | $Env:COPYRIGHT = 'Copyright (c) Xen Project.'
44 | }
45 |
46 | if ($null -eq $PackageVersions) {
47 | $PackageVersions = @{
48 | }
49 | }
50 | if ($null -eq $PackageVersions['Product']) {
51 | $PackageVersions['Product'] = '9.1.0.0'
52 | }
53 | if ($null -eq $PackageVersions['xenbus']) {
54 | $PackageVersions['xenbus'] = $PackageVersions['Product']
55 | }
56 | if ($null -eq $PackageVersions['xencons']) {
57 | $PackageVersions['xencons'] = $PackageVersions['Product']
58 | }
59 | if ($null -eq $PackageVersions['xenhid']) {
60 | $PackageVersions['xenhid'] = $PackageVersions['Product']
61 | }
62 | if ($null -eq $PackageVersions['xeniface']) {
63 | $PackageVersions['xeniface'] = $PackageVersions['Product']
64 | }
65 | if ($null -eq $PackageVersions['xennet']) {
66 | $PackageVersions['xennet'] = $PackageVersions['Product']
67 | }
68 | if ($null -eq $PackageVersions['xenvbd']) {
69 | $PackageVersions['xenvbd'] = $PackageVersions['Product']
70 | }
71 | if ($null -eq $PackageVersions['xenvif']) {
72 | $PackageVersions['xenvif'] = $PackageVersions['Product']
73 | }
74 | if ($null -eq $PackageVersions['xenvkbd']) {
75 | $PackageVersions['xenvkbd'] = $PackageVersions['Product']
76 | }
77 | if ($null -eq $PackageVersions['XenClean']) {
78 | $PackageVersions['XenClean'] = $PackageVersions['Product']
79 | }
80 | if ($null -eq $PackageVersions['XenBootFix']) {
81 | $PackageVersions['XenBootFix'] = $PackageVersions['Product']
82 | }
83 | if ($null -eq $PackageVersions['XenGuestAgent']) {
84 | $PackageVersions['XenGuestAgent'] = $PackageVersions['Product']
85 | }
86 | if ($null -eq $PackageVersions['XenTimeProvider']) {
87 | $PackageVersions['XenTimeProvider'] = $PackageVersions['Product']
88 | }
89 |
90 | if (!$Env:MSI_UPGRADE_CODE_X86) {
91 | $Env:MSI_UPGRADE_CODE_X86 = '{10828840-D8A9-4953-B44A-1F1D3CD7ECB0}'
92 | }
93 |
94 | if (!$Env:MSI_UPGRADE_CODE_X64) {
95 | $Env:MSI_UPGRADE_CODE_X64 = '{D60FED1E-316C-41B0-B7A5-E44951A82618}'
96 | }
97 |
--------------------------------------------------------------------------------
/scripts/artifact.psm1:
--------------------------------------------------------------------------------
1 | $ErrorActionPreference = 'Stop'
2 |
3 | function Get-ArtifactCatalog {
4 | [CmdletBinding()]
5 | param (
6 | [Parameter(Mandatory)]
7 | [string]$Path,
8 | [Parameter()]
9 | [string]$Filter,
10 | [Parameter()]
11 | [string[]]$Include,
12 | [Parameter()]
13 | [string[]]$Exclude
14 | )
15 |
16 | try {
17 | Push-Location $Path | Out-Null
18 | return (Get-ChildItem -Recurse -File -Filter $Filter -Include $Include -Exclude $Exclude |
19 | Resolve-Path -Relative |
20 | ForEach-Object {
21 | [PSCustomObject]@{
22 | Path = $_
23 | AuthenticodeHash = (Get-AppLockerFileInformation $_).Hash.ToString()
24 | }
25 | })
26 | }
27 | finally {
28 | Pop-Location | Out-Null
29 | }
30 | }
31 |
32 | function Compare-Artifact {
33 | [CmdletBinding()]
34 | param (
35 | [Parameter(Mandatory)]
36 | [string]$TrustedPath,
37 | [Parameter(Mandatory)]
38 | [string]$ComparePath,
39 | [Parameter()]
40 | [string]$Filter,
41 | [Parameter()]
42 | [string[]]$Include,
43 | [Parameter()]
44 | [string[]]$Exclude
45 | )
46 |
47 | $trustedCatalog = Get-ArtifactCatalog -Path $TrustedPath -Filter $Filter -Include $Include -Exclude $Exclude
48 | $compareCatalog = Get-ArtifactCatalog -Path $ComparePath -Filter $Filter -Include $Include -Exclude $Exclude
49 |
50 | $differences = Compare-Object `
51 | -ReferenceObject $trustedCatalog `
52 | -DifferenceObject $compareCatalog `
53 | -Property Path, AuthenticodeHash
54 | if ($differences) {
55 | Write-Warning "The two catalogs differ"
56 | $differences | Write-Warning
57 | return $false
58 | }
59 |
60 | return $true
61 | }
62 |
63 | function Test-ArtifactCatalog {
64 | [CmdletBinding()]
65 | param (
66 | [Parameter(Mandatory)]
67 | [object[]]$TrustedCatalog,
68 | [Parameter(Mandatory)]
69 | [string]$ComparePath,
70 | [Parameter(Mandatory)]
71 | [string]$CatalogPrefix,
72 | [Parameter()]
73 | [string]$Filter,
74 | [Parameter()]
75 | [string[]]$Include,
76 | [Parameter()]
77 | [string[]]$Exclude,
78 | [Parameter()]
79 | [switch]$IncludeEqual
80 | )
81 |
82 | $filteredTrustedCatalog = $TrustedCatalog | Where-Object {
83 | $_.Path.StartsWith($CatalogPrefix, [System.StringComparison]::OrdinalIgnoreCase)
84 | }
85 |
86 | $compareCatalog = Get-ArtifactCatalog -Path $ComparePath -Filter $Filter -Include $Include -Exclude $Exclude |
87 | ForEach-Object {
88 | $compareFile = Join-Path $CatalogPrefix $_.Path.TrimStart([char]'.', [System.IO.Path]::DirectorySeparatorChar, [System.IO.Path]::AltDirectorySeparatorChar)
89 | Write-Verbose "$($_.Path) -> $compareFile"
90 | [PSCustomObject]@{
91 | Path = $compareFile
92 | AuthenticodeHash = $_.AuthenticodeHash
93 | }
94 | }
95 |
96 | Compare-Object `
97 | -ReferenceObject $filteredTrustedCatalog `
98 | -DifferenceObject $compareCatalog `
99 | -Property Path, AuthenticodeHash `
100 | -IncludeEqual:$IncludeEqual
101 | }
102 |
--------------------------------------------------------------------------------
/DriverInstallCustomAction/ImmediateActions.cs:
--------------------------------------------------------------------------------
1 | using Windows.Win32;
2 | using Windows.Win32.Foundation;
3 | using Windows.Win32.Devices.DeviceAndDriverInstallation;
4 | using WixToolset.Dtf.WindowsInstaller;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using XenDriverUtils;
9 |
10 | namespace XenInstCA {
11 | public static class ImmediateActions {
12 | [CustomAction]
13 | public static ActionResult CheckWindowsVersion(Session session) {
14 | var minSupportedProperty = session["XenMinSupportedVersion"];
15 | session.Log($"minSupportedProperty {minSupportedProperty}");
16 | if (!Version.TryParse(minSupportedProperty, out var minSupported)) {
17 | return ActionResult.Success;
18 | }
19 | session.Log($"minSupported {minSupported}");
20 |
21 | var currentVersion = VersionUtils.GetWindowsVersion();
22 | session.Log($"currentVersion {currentVersion}");
23 | if (currentVersion < minSupported) {
24 | session["XenWindowsNotSupported"] = currentVersion.ToString();
25 | }
26 | return ActionResult.Success;
27 | }
28 |
29 | [CustomAction]
30 | public static ActionResult CheckReboot(Session session) {
31 | if (CustomActionUtils.IsRebootScheduled()) {
32 | session.SetMode(InstallRunMode.RebootAtEnd, true);
33 | }
34 | return ActionResult.Success;
35 | }
36 |
37 | [CustomAction]
38 | public static ActionResult CheckIncompatibleDevices(Session session) {
39 | using var logScope = new LoggerScope(new MsiSessionLogger(session));
40 |
41 | var incompatibilities = new List();
42 |
43 | var devInfo = PInvoke.SetupDiGetClassDevs(
44 | (Guid?)null,
45 | null,
46 | HWND.Null,
47 | SETUP_DI_GET_CLASS_DEVS_FLAGS.DIGCF_ALLCLASSES);
48 | foreach (var devInfoData in DriverUtils.EnumerateDevices(devInfo)) {
49 | List deviceIds = DriverUtils.GetDeviceHardwareAndCompatibleIds(devInfo, devInfoData);
50 | bool found = false;
51 | foreach (var xenClass in XenDeviceInfo.KnownDevices.Values) {
52 | if (deviceIds.Any(x => xenClass.MatchesId(x, checkKnown: false, checkIncompatible: true))) {
53 | found = true;
54 | break;
55 | }
56 | }
57 | if (!found) {
58 | continue;
59 | }
60 |
61 | Logger.Log($"Found device with incompatible IDs: {string.Join(",", deviceIds)}");
62 | var instanceId = DriverUtils.GetDeviceInstanceId(devInfo, devInfoData);
63 | if (instanceId != null) {
64 | Logger.Log($"Adding incompatible instance ID {instanceId}");
65 | incompatibilities.Add(instanceId);
66 | } else {
67 | incompatibilities.Add("(unknown)");
68 | }
69 | }
70 |
71 | session["IncompatibleDevices"] = string.Join(",", incompatibilities);
72 | return ActionResult.Success;
73 | }
74 |
75 | [CustomAction]
76 | public static ActionResult Check3PStorageDrivers(Session session) {
77 | using var logScope = new LoggerScope(new MsiSessionLogger(session));
78 |
79 | var found3PDrivers = XenCleanup.Find3PStorageDrivers();
80 | session["ThirdPartyStorageDrivers"] = string.Join(",", found3PDrivers.Select(x => x.DriverInfPath));
81 | return ActionResult.Success;
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/installer/Package.wxs:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
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 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/installer/UI/XenCustomizeDlg.wxs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/extras/xsa468.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 10.0.0.0
4 | {2E07F7E4-194C-4D20-B7C9-6F44A6C5A234}
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 | 0
89 | {1A785F78-1622-4A2E-8866-34A6F87CF9F7}
90 | {1A785F78-1622-4A2E-8866-34A6F87CF9F7}
91 |
92 |
--------------------------------------------------------------------------------
/installer/UI/XenDriversUI.wxs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
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 |
--------------------------------------------------------------------------------
/scripts/branding-ci.ps1:
--------------------------------------------------------------------------------
1 | # Prepare branding info from provided environment variables.
2 | # This script should be attestation-safe. Try our best to sanitize the environment.
3 | # Use a highly-constrained parameter range.
4 |
5 | #Requires -Version 7
6 |
7 | [CmdletBinding()]
8 | param (
9 | [Parameter(Mandatory)]
10 | [string]$OutFile,
11 | [Parameter()]
12 | [switch]$AddSigner
13 | )
14 |
15 | $ErrorActionPreference = 'Stop'
16 |
17 | function Out-SafeString {
18 | [CmdletBinding()]
19 | param (
20 | [Parameter()]
21 | [string]$InputObject,
22 | [Parameter(Mandatory)]
23 | [ValidateSet("Version", "VendorPrefix", "PathSafe", "Freeform", "Hex", "Guid", "Base64", "OneOrEmpty")]
24 | [string]$PatternType
25 | )
26 |
27 | Write-Verbose "InputObject '$InputObject'"
28 | Write-Verbose "PatternType '$PatternType'"
29 |
30 | # Allow basic punctuations only. All strings must be single-quote safe.
31 | $AllowedPatterns = @{
32 | "Version" = '^[0-9]+(\.[0-9]+){0,3}$'
33 | "VendorPrefix" = '^[a-z][a-z0-9]{0,3}$'
34 | "PathSafe" = '^[a-z0-9.,-_ ]*$'
35 | "Freeform" = '^[a-z0-9().,-_ ]*$'
36 | "Hex" = '^[a-f0-9]*$'
37 | "Guid" = '^\{[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}\}$'
38 | "Base64" = '^[a-z0-9+/=\r\n\t ]*$'
39 | "OneOrEmpty" = '^1?$'
40 | }
41 |
42 | if ($InputObject -isnot [string]) {
43 | throw "Invalid input"
44 | }
45 | $Pattern = $AllowedPatterns[$PatternType]
46 | if (!$PatternType) {
47 | throw "Invalid pattern type $PatternType"
48 | }
49 |
50 | if (!($InputObject -imatch $Pattern)) {
51 | throw "Invalid input for pattern type $PatternType"
52 | }
53 |
54 | return $InputObject
55 | }
56 |
57 | # Branding file is emitted as step summary. Don't put any secrets here!
58 | $content = @"
59 | `$Env:VENDOR_NAME = '$(Out-SafeString -PatternType PathSafe -InputObject $Env:VENDOR_NAME)'
60 | `$Env:PRODUCT_NAME = '$(Out-SafeString -PatternType PathSafe -InputObject $Env:PRODUCT_NAME)'
61 | `$Env:VENDOR_PREFIX = '$(Out-SafeString -PatternType VendorPrefix -InputObject $Env:VENDOR_PREFIX)'
62 | `$Env:COPYRIGHT = '$(Out-SafeString -PatternType Freeform -InputObject $Env:COPYRIGHT)'
63 |
64 | `$Env:FORCE_ACTIVATE = '$(Out-SafeString -PatternType OneOrEmpty -InputObject $Env:FORCE_ACTIVATE)'
65 | `$Env:FORCE_UNPLUG = '$(Out-SafeString -PatternType OneOrEmpty -InputObject $Env:FORCE_UNPLUG)'
66 |
67 | `$PackageVersions = @{
68 | Product = '$(Out-SafeString -PatternType Version -InputObject $Env:PackageVersions_Product)'
69 | xenbus = '$(Out-SafeString -PatternType Version -InputObject $Env:PackageVersions_xenbus)'
70 | xencons = '$(Out-SafeString -PatternType Version -InputObject $Env:PackageVersions_xencons)'
71 | xenhid = '$(Out-SafeString -PatternType Version -InputObject $Env:PackageVersions_xenhid)'
72 | xeniface = '$(Out-SafeString -PatternType Version -InputObject $Env:PackageVersions_xeniface)'
73 | xennet = '$(Out-SafeString -PatternType Version -InputObject $Env:PackageVersions_xennet)'
74 | xenvbd = '$(Out-SafeString -PatternType Version -InputObject $Env:PackageVersions_xenvbd)'
75 | xenvif = '$(Out-SafeString -PatternType Version -InputObject $Env:PackageVersions_xenvif)'
76 | xenvkbd = '$(Out-SafeString -PatternType Version -InputObject $Env:PackageVersions_xenvkbd)'
77 | XenClean = '$(Out-SafeString -PatternType Version -InputObject $Env:PackageVersions_XenClean)'
78 | XenBootFix = '$(Out-SafeString -PatternType Version -InputObject $Env:PackageVersions_XenBootFix)'
79 | XenGuestAgent = '$(Out-SafeString -PatternType Version -InputObject $Env:PackageVersions_XenGuestAgent)'
80 | XenTimeProvider = '$(Out-SafeString -PatternType Version -InputObject $Env:PackageVersions_XenTimeProvider)'
81 | }
82 |
83 | `$Env:MSI_UPGRADE_CODE_X86 = '$(Out-SafeString -PatternType Guid -InputObject $Env:MSI_UPGRADE_CODE_X86)'
84 | `$Env:MSI_UPGRADE_CODE_X64 = '$(Out-SafeString -PatternType Guid -InputObject $Env:MSI_UPGRADE_CODE_X64)'
85 | "@
86 | Set-Content -Path $OutFile -Value $content -Force
87 |
88 | if ($AddSigner) {
89 | # Doesn't hurt to verify again.
90 | Out-SafeString -PatternType Base64 -InputObject $Env:SIGNER_PFX_BASE64 | Out-Null
91 | & "$PSScriptRoot\signer-ci.ps1" -OutFile $OutFile
92 | }
93 |
94 | Add-Content -Path $Env:GITHUB_STEP_SUMMARY -Value "Branding:", "``````", $content, "``````" -Force
95 |
--------------------------------------------------------------------------------
/.github/workflows/build-drivers.yml:
--------------------------------------------------------------------------------
1 | name: build-drivers
2 |
3 | on:
4 | workflow_call:
5 | workflow_dispatch:
6 |
7 | jobs:
8 | build-drivers:
9 | runs-on: windows-2022
10 | steps:
11 | - name: Checkout
12 | uses: actions/checkout@v4
13 | with:
14 | submodules: true
15 | - name: Add MSBuild
16 | uses: microsoft/setup-msbuild@v2
17 | - name: Configure branding
18 | run: .\scripts\branding-ci.ps1 -OutFile .\branding.ps1
19 | env:
20 | VENDOR_NAME: ${{vars.VENDOR_NAME}}
21 | PRODUCT_NAME: ${{vars.PRODUCT_NAME}}
22 | VENDOR_PREFIX: ${{vars.VENDOR_PREFIX}}
23 | COPYRIGHT: ${{vars.COPYRIGHT}}
24 | FORCE_ACTIVATE: ${{vars.FORCE_ACTIVATE}}
25 | FORCE_UNPLUG: ${{vars.FORCE_UNPLUG}}
26 | PackageVersions_Product: ${{vars.PackageVersions_Product}}.${{github.run_number}}
27 | PackageVersions_xenbus: ${{vars.PackageVersions_xenbus}}.${{github.run_number}}
28 | PackageVersions_xencons: ${{vars.PackageVersions_xencons}}.${{github.run_number}}
29 | PackageVersions_xenhid: ${{vars.PackageVersions_xenhid}}.${{github.run_number}}
30 | PackageVersions_xeniface: ${{vars.PackageVersions_xeniface}}.${{github.run_number}}
31 | PackageVersions_xennet: ${{vars.PackageVersions_xennet}}.${{github.run_number}}
32 | PackageVersions_xenvbd: ${{vars.PackageVersions_xenvbd}}.${{github.run_number}}
33 | PackageVersions_xenvif: ${{vars.PackageVersions_xenvif}}.${{github.run_number}}
34 | PackageVersions_xenvkbd: ${{vars.PackageVersions_xenvkbd}}.${{github.run_number}}
35 | PackageVersions_XenClean: ${{vars.PackageVersions_XenClean}}.${{github.run_number}}
36 | PackageVersions_XenBootFix: ${{vars.PackageVersions_XenBootFix}}.${{github.run_number}}
37 | PackageVersions_XenGuestAgent: ${{vars.PackageVersions_XenGuestAgent}}.${{github.run_number}}
38 | PackageVersions_XenTimeProvider: ${{vars.PackageVersions_XenTimeProvider}}.${{github.run_number}}
39 | MSI_UPGRADE_CODE_X86: ${{vars.MSI_UPGRADE_CODE_X86}}
40 | MSI_UPGRADE_CODE_X64: ${{vars.MSI_UPGRADE_CODE_X64}}
41 | - name: Build drivers
42 | run: .\build-drivers.ps1 -Configuration Release -Platform x64
43 | - name: Catalog drivers
44 | run: |
45 | Import-Module .\scripts\artifact.psm1 -Force
46 | Get-ArtifactCatalog -Path .\installer\output -Include *.sys, *.dll, *.exe, *.inf |
47 | Export-Csv -NoTypeInformation drivers-catalog.csv
48 | - name: Upload drivers
49 | id: upload
50 | uses: actions/upload-artifact@v5
51 | with:
52 | path: installer/output/
53 | name: drivers
54 | - name: Upload drivers catalog
55 | uses: actions/upload-artifact@v5
56 | with:
57 | path: drivers-catalog.csv
58 | name: drivers-catalog
59 | outputs:
60 | subject-name: drivers
61 | subject-digest: sha256:${{steps.upload.outputs.artifact-digest}}
62 | catalog-name: drivers-catalog
63 |
64 | attest-drivers:
65 | needs: [build-drivers]
66 | uses: ./.github/workflows/attest.yml
67 | permissions:
68 | id-token: write
69 | contents: read
70 | attestations: write
71 | with:
72 | subject-name: ${{needs.build-drivers.outputs.subject-name}}
73 | subject-digest: ${{needs.build-drivers.outputs.subject-digest}}
74 | catalog-name: ${{needs.build-drivers.outputs.catalog-name}}
75 |
76 | selfsign-drivers:
77 | needs: [build-drivers]
78 | runs-on: windows-latest
79 | steps:
80 | # for the sign scripts
81 | - name: Checkout
82 | uses: actions/checkout@v4
83 | - name: Download artifact
84 | uses: actions/download-artifact@v6
85 | with:
86 | path: installer/output/
87 | name: drivers
88 | - name: Install certificate
89 | run: .\scripts\signer-ci.ps1 -OutFile .\branding.ps1
90 | env:
91 | SIGNER_PFX_BASE64: ${{secrets.SIGNER_PFX_BASE64}}
92 | - name: Sign artifact
93 | run: |
94 | . .\branding.ps1
95 | . .\scripts\sign.ps1
96 | Set-SignerFileSignature (Get-ChildItem .\installer\output -File -Recurse -Include *.sys, *.dll, *.exe, *.cat)
97 | - name: Clean up certificates
98 | if: always()
99 | run: Remove-Item Cert:\CurrentUser\My\* -ErrorAction SilentlyContinue
100 | - name: Upload artifact
101 | id: upload
102 | uses: actions/upload-artifact@v5
103 | with:
104 | path: installer/output/
105 | name: drivers-signed
106 |
--------------------------------------------------------------------------------
/.github/workflows/build-timeprovider.yml:
--------------------------------------------------------------------------------
1 | name: build-timeprovider
2 |
3 | on:
4 | workflow_call:
5 | workflow_dispatch:
6 |
7 | jobs:
8 | build-timeprovider:
9 | runs-on: windows-latest
10 | steps:
11 | - name: Checkout
12 | uses: actions/checkout@v4
13 | with:
14 | submodules: true
15 | - name: Add MSBuild
16 | uses: microsoft/setup-msbuild@v2
17 | - name: Configure branding
18 | run: .\scripts\branding-ci.ps1 -OutFile .\branding.ps1
19 | env:
20 | VENDOR_NAME: ${{vars.VENDOR_NAME}}
21 | PRODUCT_NAME: ${{vars.PRODUCT_NAME}}
22 | VENDOR_PREFIX: ${{vars.VENDOR_PREFIX}}
23 | COPYRIGHT: ${{vars.COPYRIGHT}}
24 | FORCE_ACTIVATE: ${{vars.FORCE_ACTIVATE}}
25 | FORCE_UNPLUG: ${{vars.FORCE_UNPLUG}}
26 | PackageVersions_Product: ${{vars.PackageVersions_Product}}.${{github.run_number}}
27 | PackageVersions_xenbus: ${{vars.PackageVersions_xenbus}}.${{github.run_number}}
28 | PackageVersions_xencons: ${{vars.PackageVersions_xencons}}.${{github.run_number}}
29 | PackageVersions_xenhid: ${{vars.PackageVersions_xenhid}}.${{github.run_number}}
30 | PackageVersions_xeniface: ${{vars.PackageVersions_xeniface}}.${{github.run_number}}
31 | PackageVersions_xennet: ${{vars.PackageVersions_xennet}}.${{github.run_number}}
32 | PackageVersions_xenvbd: ${{vars.PackageVersions_xenvbd}}.${{github.run_number}}
33 | PackageVersions_xenvif: ${{vars.PackageVersions_xenvif}}.${{github.run_number}}
34 | PackageVersions_xenvkbd: ${{vars.PackageVersions_xenvkbd}}.${{github.run_number}}
35 | PackageVersions_XenClean: ${{vars.PackageVersions_XenClean}}.${{github.run_number}}
36 | PackageVersions_XenBootFix: ${{vars.PackageVersions_XenBootFix}}.${{github.run_number}}
37 | PackageVersions_XenGuestAgent: ${{vars.PackageVersions_XenGuestAgent}}.${{github.run_number}}
38 | PackageVersions_XenTimeProvider: ${{vars.PackageVersions_XenTimeProvider}}.${{github.run_number}}
39 | MSI_UPGRADE_CODE_X86: ${{vars.MSI_UPGRADE_CODE_X86}}
40 | MSI_UPGRADE_CODE_X64: ${{vars.MSI_UPGRADE_CODE_X64}}
41 | - name: Build time provider
42 | run: .\build-timeprovider.ps1 -Configuration Release -Platform x64
43 | - name: Catalog time provider
44 | run: |
45 | Import-Module .\scripts\artifact.psm1 -Force
46 | Get-ArtifactCatalog -Path .\xentimeprovider\x64\Release -Include *.dll |
47 | Export-Csv -NoTypeInformation timeprovider-catalog.csv
48 | - name: Upload time provider
49 | id: upload
50 | uses: actions/upload-artifact@v5
51 | with:
52 | path: xentimeprovider/x64/Release/
53 | name: timeprovider
54 | - name: Upload timeprovider catalog
55 | uses: actions/upload-artifact@v5
56 | with:
57 | path: timeprovider-catalog.csv
58 | name: timeprovider-catalog
59 | outputs:
60 | subject-name: timeprovider
61 | subject-digest: sha256:${{steps.upload.outputs.artifact-digest}}
62 | catalog-name: timeprovider-catalog
63 |
64 | attest-timeprovider:
65 | needs: [build-timeprovider]
66 | uses: ./.github/workflows/attest.yml
67 | permissions:
68 | id-token: write
69 | contents: read
70 | attestations: write
71 | with:
72 | subject-name: ${{needs.build-timeprovider.outputs.subject-name}}
73 | subject-digest: ${{needs.build-timeprovider.outputs.subject-digest}}
74 | catalog-name: ${{needs.build-timeprovider.outputs.catalog-name}}
75 |
76 | selfsign-timeprovider:
77 | needs: [build-timeprovider]
78 | runs-on: windows-latest
79 | steps:
80 | # for the sign scripts
81 | - name: Checkout
82 | uses: actions/checkout@v4
83 | - name: Download artifact
84 | uses: actions/download-artifact@v6
85 | with:
86 | path: xentimeprovider/x64/Release/
87 | name: timeprovider
88 | - name: Install certificate
89 | run: .\scripts\signer-ci.ps1 -OutFile .\branding.ps1
90 | env:
91 | SIGNER_PFX_BASE64: ${{secrets.SIGNER_PFX_BASE64}}
92 | - name: Sign artifact
93 | run: |
94 | . .\branding.ps1
95 | . .\scripts\sign.ps1
96 | Set-SignerFileSignature (Get-ChildItem .\xentimeprovider\x64\Release -File -Recurse -Include *.dll)
97 | - name: Clean up certificates
98 | if: always()
99 | run: Remove-Item Cert:\CurrentUser\My\* -ErrorAction SilentlyContinue
100 | - name: Upload artifact
101 | id: upload
102 | uses: actions/upload-artifact@v5
103 | with:
104 | path: xentimeprovider/x64/Release/
105 | name: timeprovider-signed
106 |
--------------------------------------------------------------------------------
/docs/build.md:
--------------------------------------------------------------------------------
1 | # Build guide
2 |
3 | ## Requirements
4 |
5 | * Visual Studio 2022 with the following features:
6 | * Desktop development with C++ workload
7 | * .NET desktop development workload
8 | * .NET Framework 4.6.2 targeting pack
9 | * MSVC v143 - VS 2022 C++ x64/x86 Spectre-mitigated libs (Latest)
10 | * Windows Driver Kit extensions for Visual Studio (listed simply as "Windows Driver Kit" in the Visual Studio Installer)
11 | * Windows SDK for Windows 10/11 (tested with 10.0.22621 and 10.0.26100)
12 | * Windows Driver Kit matching your Windows SDK
13 | * Git for Windows
14 | * Rustup and latest Rust stable
15 | * [Microsoft SBOM Tool](https://github.com/microsoft/sbom-tool) available as `sbom.exe` in PATH (installable via WinGet)
16 |
17 | Windows SDK and WDK dependencies can be found on [Microsoft Learn](https://learn.microsoft.com/en-us/windows-hardware/drivers/other-wdk-downloads).
18 |
19 | ## Branding and configuration
20 |
21 | You customize the driver and installer package by creating `branding.ps1` inside the repository root.
22 |
23 | See below for an example of the `branding.ps1` file.
24 |
25 | ```powershell
26 | $Env:VENDOR_NAME = 'Xen Project'
27 | $Env:PRODUCT_NAME = 'Xen'
28 | $Env:VENDOR_PREFIX = 'XP'
29 | $Env:COPYRIGHT = 'Copyright (c) Xen Project.'
30 |
31 | # These version numbers are used in multiple places.
32 | # You must have at least 2 version components (major.minor).
33 | # The third and fourth components will be 0-filled if absent, except in drivers where the revision will be the build time prefixed with "1".
34 | # For compatibility reasons, it's not recommended to have any version component exceed 255.255.65535.65535.
35 | $PackageVersions = @{
36 | Product = '9.0.9000.0'
37 | xenbus = '9.0.9001.0' # defaults to product version
38 | xencons = '9.0.9002.0' # defaults to product version
39 | xenhid = '9.0.9003.0' # defaults to product version
40 | xeniface = '9.0.9004.0' # defaults to product version
41 | xennet = '9.0.9005.0' # defaults to product version
42 | xenvbd = '9.0.9006.0' # defaults to product version
43 | xenvif = '9.0.9007.0' # defaults to product version
44 | xenvkbd = '9.0.9008.0' # defaults to product version
45 | XenClean = '9.0.9009.0' # defaults to product version
46 | XenBootFix = '9.0.9010.0' # defaults to product version
47 | XenGuestAgent = '9.0.9011.0' # defaults to product version
48 | XenTimeProvider = '9.0.9012.0' # defaults to product version
49 | }
50 |
51 | # These variables influence the UpgradeCode property of generated MSI packages.
52 | # To avoid conflict between installers of different vendors, you must change
53 | # these values to a random GUID if building your own installer.
54 | $Env:MSI_UPGRADE_CODE_X86 = '{GUIDHERE-GUID-HERE-GUID-HEREGUIDHERE}'
55 | $Env:MSI_UPGRADE_CODE_X64 = '{GUIDHERE-GUID-HERE-GUID-HEREGUIDHERE}'
56 |
57 | $Env:SIGNER = ""
58 | ```
59 |
60 | Specify `$Env:SIGNER` in `branding.ps1` to choose a specific signing certificate thumbprint or PFX path if necessary.
61 | (A test certificate will be used for the drivers otherwise)
62 |
63 | ## Building drivers
64 |
65 | Using the x64 Native Tools Command Prompt for VS 2022, navigate to this repository and build the drivers:
66 |
67 | ```powershell
68 | .\build-drivers.ps1 -Configuration Release -Platform x64
69 | ```
70 |
71 | Output drivers will be collected in `installer\output`.
72 |
73 | If you need to sign the drivers externally (e.g. WHQL signatures), you must replace the drivers found here with your own signed binaries.
74 | These binaries should be located at `installer\output\\\` for each driver included in the package.
75 |
76 | ## Building the Rust-based Windows guest agent
77 |
78 | Simply run the command:
79 |
80 | ```powershell
81 | .\build-guestagent.ps1 -Configuration release
82 | ```
83 |
84 | The binaries are located at `xen-guest-agent\target\`.
85 |
86 | ## Building the Xen time provider
87 |
88 | Run the command:
89 |
90 | ```powershell
91 | .\build-timeprovider.ps1 -Configuration Release
92 | ```
93 |
94 | The binaries are located at `xentimeprovider\\`.
95 |
96 | ## Building the installer and release package
97 |
98 | Next, build the installer and XenClean packages:
99 |
100 | ```powershell
101 | .\build-installer.ps1 -Configuration Release -Platform x64 -Target Rebuild
102 | ```
103 |
104 | By default, output files will be dropped in the `output\--` directory.
105 | You may specify `-ExportSymbols` or `-ExportCertificate` to include the debug symbols and signer certificate (along with testsigning scripts) in your output package respectively.
106 |
--------------------------------------------------------------------------------
/docs/attestation.md:
--------------------------------------------------------------------------------
1 | # Artifact attestation
2 |
3 | Artifact attestation helps establish a link between the built binaries and their corresponding source code.
4 | In the context of XCP-ng Windows Guest Tools (WinPV for short), our artifact attestation aims to extend this link to signed binaries (WHQL-signed drivers, EV-signed guest agent, etc.)
5 | With this, even signed binaries benefit from an attested build pipeline.
6 |
7 | ## How to verify binaries
8 |
9 | ### Step 1: Obtain attested catalog
10 |
11 | Each release includes an attested catalog (`drivers-catalog.csv`, `guestagent-catalog.csv`).
12 | You can find them in the Assets section of each release.
13 |
14 | ### Step 2: Verify catalog attestation
15 |
16 | Use the GitHub CLI to verify the downloaded catalog for correspondence with its source code.
17 | For example:
18 |
19 | ```
20 | $ gh attestation verify drivers-catalog.csv -R xcp-ng/win-pv-drivers --deny-self-hosted-runners
21 | Loaded digest sha256:... for file://drivers-catalog.csv
22 | Loaded 1 attestation from GitHub API
23 |
24 | The following policy criteria will be enforced:
25 | - Predicate type must match:..................... https://slsa.dev/provenance/v1
26 | - Source Repository Owner URI must match:........ https://github.com/xcp-ng
27 | - Source Repository URI must match:.............. https://github.com/xcp-ng/win-pv-drivers
28 | - Subject Alternative Name must match regex:..... (?i)^https://github.com/xcp-ng/win-pv-drivers/
29 | - OIDC Issuer must match:........................ https://token.actions.githubusercontent.com
30 | - Action workflow Runner Environment must match : github-hosted
31 |
32 | ✓ Verification succeeded!
33 |
34 | The following 1 attestation matched the policy criteria
35 |
36 | - Attestation #1
37 | - Build repo:..... xcp-ng/win-pv-drivers
38 | - Build workflow:. .github/workflows/build-installer.yml@...
39 | - Signer repo:.... xcp-ng/win-pv-drivers
40 | - Signer workflow: .github/workflows/attest.yml@...
41 | ```
42 |
43 | Alternatively, you can verify the catalog by using the attestation link provided with each release.
44 |
45 | ### Step 3: Verify final binaries
46 |
47 | Use the [artifact.psm1](/scripts/artifact.psm1) PowerShell module to verify the signed binaries:
48 |
49 | ```powershell
50 | > Import-Module scripts\artifact.psm1 -Force
51 |
52 | > Test-ArtifactCatalog -TrustedCatalog (Import-Csv .\drivers-catalog.csv) -ComparePath .\drivers-signed\xenbus -CatalogPrefix .\xenbus\x64\Release -Include *.sys, *.dll, *.exe, *.inf
53 | ```
54 |
55 | **Tip**: You can verify parts of the catalog by choosing `CatalogPrefix` accordingly.
56 |
57 | ## Implementation details
58 |
59 | WinPV's build and attestation machinery is provided by [GitHub Actions](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds).
60 |
61 | Our build pipeline consists of 3 workflows:
62 | [build-drivers](/.github/workflows/build-drivers.yml),
63 | [build-guestagent](/.github/workflows/build-guestagent.yml) and
64 | [build-installer](/.github/workflows/build-installer.yml).
65 | The first two provide both unsigned (but attested) and testsigned binaries using a certificate supplied through secrets;
66 | the latter provides a complete installer using testsigned binaries.
67 |
68 | Here are the general build steps for each workflow:
69 |
70 | 1. Inject branding information
71 | 2. Execute build script
72 | 3. Generate catalog
73 | 4. Attest build and catalog
74 | 5. Sign build
75 |
76 | See below for explanations of specific elements of the build steps.
77 |
78 | ### Branding information and attestation safety
79 |
80 | In normal builds, branding is injected via [branding.ps1](/docs/build.md).
81 | However, this route is not secure against malicious instructions injected via the branding file.
82 |
83 | To mitigate this issue, we inject branding via a [sanitization script](/scripts/branding-ci.ps1), which verifies that branding variables follow an authorized pattern. Signing certificates are injected via a [similar script](/scripts/signer-ci.ps1).
84 |
85 | ### Catalog generation
86 |
87 | Standard attestation protects an artifact's flat hash.
88 | Once a binary is attested with GitHub, it cannot be modified whatsoever without breaking the attestation, including re-signing the binary with another certificate.
89 |
90 | To extend the attestation to re-signed binaries, we use a *catalog file* (not the same as Windows `.cat` catalogs).
91 | Catalog files are simple CSV files containing the relative path of each artifact and its Authenticode hash.
92 | We use Authenticode hashes instead of flat hashes to avoid breaking the attestation when re-signing.
93 | The catalog file itself is directly attested with GitHub Actions every build.
94 |
95 | ### Catalog verification
96 |
97 | `Test-ArtifactCatalog` provides catalog filtering, prefix stripping and path normalization features.
98 | See `artifact.psm1` for details.
99 |
--------------------------------------------------------------------------------
/.github/workflows/build-guestagent.yml:
--------------------------------------------------------------------------------
1 | name: build-guestagent
2 |
3 | on:
4 | workflow_call:
5 | workflow_dispatch:
6 |
7 | jobs:
8 | build-guestagent:
9 | runs-on: windows-latest
10 | env:
11 | X_CARGO_AUDITABLE_VERSION: 0.7.2
12 | steps:
13 | - name: Restore cargo auditable
14 | id: restore-cargo-auditable
15 | uses: actions/cache/restore@v4
16 | with:
17 | path: ~\.cargo\bin\
18 | key: ${{runner.os}}-cargo-auditable-${{env.X_CARGO_AUDITABLE_VERSION}}
19 | - name: Install cargo auditable
20 | if: steps.restore-cargo-auditable.outputs.cache-hit != 'true'
21 | run: cargo install cargo-auditable@${{env.X_CARGO_AUDITABLE_VERSION}} --locked
22 | - name: Save cargo auditable
23 | if: steps.restore-cargo-auditable.outputs.cache-hit != 'true'
24 | uses: actions/cache/save@v4
25 | with:
26 | path: ~\.cargo\bin\
27 | key: ${{steps.restore-cargo-auditable.outputs.cache-primary-key}}
28 | - name: Checkout
29 | uses: actions/checkout@v4
30 | with:
31 | submodules: true
32 | - name: Configure branding
33 | run: .\scripts\branding-ci.ps1 -OutFile .\branding.ps1
34 | env:
35 | VENDOR_NAME: ${{vars.VENDOR_NAME}}
36 | PRODUCT_NAME: ${{vars.PRODUCT_NAME}}
37 | VENDOR_PREFIX: ${{vars.VENDOR_PREFIX}}
38 | COPYRIGHT: ${{vars.COPYRIGHT}}
39 | PackageVersions_Product: ${{vars.PackageVersions_Product}}.${{github.run_number}}
40 | PackageVersions_xenbus: ${{vars.PackageVersions_xenbus}}.${{github.run_number}}
41 | PackageVersions_xencons: ${{vars.PackageVersions_xencons}}.${{github.run_number}}
42 | PackageVersions_xenhid: ${{vars.PackageVersions_xenhid}}.${{github.run_number}}
43 | PackageVersions_xeniface: ${{vars.PackageVersions_xeniface}}.${{github.run_number}}
44 | PackageVersions_xennet: ${{vars.PackageVersions_xennet}}.${{github.run_number}}
45 | PackageVersions_xenvbd: ${{vars.PackageVersions_xenvbd}}.${{github.run_number}}
46 | PackageVersions_xenvif: ${{vars.PackageVersions_xenvif}}.${{github.run_number}}
47 | PackageVersions_xenvkbd: ${{vars.PackageVersions_xenvkbd}}.${{github.run_number}}
48 | PackageVersions_XenClean: ${{vars.PackageVersions_XenClean}}.${{github.run_number}}
49 | PackageVersions_XenBootFix: ${{vars.PackageVersions_XenBootFix}}.${{github.run_number}}
50 | PackageVersions_XenGuestAgent: ${{vars.PackageVersions_XenGuestAgent}}.${{github.run_number}}
51 | PackageVersions_XenTimeProvider: ${{vars.PackageVersions_XenTimeProvider}}.${{github.run_number}}
52 | MSI_UPGRADE_CODE_X86: ${{vars.MSI_UPGRADE_CODE_X86}}
53 | MSI_UPGRADE_CODE_X64: ${{vars.MSI_UPGRADE_CODE_X64}}
54 | - name: Build guest agent
55 | run: .\build-guestagent.ps1 -Configuration release
56 | - name: Catalog guest agent
57 | run: |
58 | Import-Module .\scripts\artifact.psm1 -Force
59 | Get-ArtifactCatalog -Path .\xen-guest-agent\target\release -Include *.exe |
60 | Export-Csv -NoTypeInformation guestagent-catalog.csv
61 | - name: Upload artifact
62 | id: upload
63 | uses: actions/upload-artifact@v5
64 | with:
65 | name: guestagent
66 | path: |
67 | xen-guest-agent/target/release/*.exe
68 | xen-guest-agent/target/release/*.pdb
69 | - name: Upload guest agent catalog
70 | uses: actions/upload-artifact@v5
71 | with:
72 | path: guestagent-catalog.csv
73 | name: guestagent-catalog
74 | outputs:
75 | subject-name: guestagent
76 | subject-digest: sha256:${{steps.upload.outputs.artifact-digest}}
77 | catalog-name: guestagent-catalog
78 |
79 | attest-guestagent:
80 | needs: [build-guestagent]
81 | uses: ./.github/workflows/attest.yml
82 | permissions:
83 | id-token: write
84 | contents: read
85 | attestations: write
86 | with:
87 | subject-name: ${{needs.build-guestagent.outputs.subject-name}}
88 | subject-digest: ${{needs.build-guestagent.outputs.subject-digest}}
89 | catalog-name: ${{needs.build-guestagent.outputs.catalog-name}}
90 |
91 | selfsign-guestagent:
92 | needs: [build-guestagent]
93 | runs-on: windows-latest
94 | steps:
95 | # for the sign scripts
96 | - name: Checkout
97 | uses: actions/checkout@v4
98 | - name: Download artifact
99 | uses: actions/download-artifact@v6
100 | with:
101 | path: xen-guest-agent/target/release/
102 | name: guestagent
103 | - name: Install certificate
104 | run: .\scripts\signer-ci.ps1 -OutFile .\branding.ps1
105 | env:
106 | SIGNER_PFX_BASE64: ${{secrets.SIGNER_PFX_BASE64}}
107 | - name: Sign artifact
108 | run: |
109 | . .\branding.ps1
110 | . .\scripts\sign.ps1
111 | Set-SignerFileSignature (Get-ChildItem .\xen-guest-agent\target\release -File -Recurse -Include *.dll, *.exe)
112 | - name: Clean up certificates
113 | if: always()
114 | run: Remove-Item Cert:\CurrentUser\My\* -ErrorAction SilentlyContinue
115 | - name: Upload artifact
116 | id: upload
117 | uses: actions/upload-artifact@v5
118 | with:
119 | path: xen-guest-agent/target/release/
120 | name: guestagent-signed
121 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.{cs,vb}]
2 | #### Naming styles ####
3 |
4 | # Naming rules
5 |
6 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
7 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
8 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
9 |
10 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
11 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types
12 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
13 |
14 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
15 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
16 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
17 |
18 | # Symbol specifications
19 |
20 | dotnet_naming_symbols.interface.applicable_kinds = interface
21 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
22 | dotnet_naming_symbols.interface.required_modifiers =
23 |
24 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
25 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
26 | dotnet_naming_symbols.types.required_modifiers =
27 |
28 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
29 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
30 | dotnet_naming_symbols.non_field_members.required_modifiers =
31 |
32 | # Naming styles
33 |
34 | dotnet_naming_style.begins_with_i.required_prefix = I
35 | dotnet_naming_style.begins_with_i.required_suffix =
36 | dotnet_naming_style.begins_with_i.word_separator =
37 | dotnet_naming_style.begins_with_i.capitalization = pascal_case
38 |
39 | dotnet_naming_style.pascal_case.required_prefix =
40 | dotnet_naming_style.pascal_case.required_suffix =
41 | dotnet_naming_style.pascal_case.word_separator =
42 | dotnet_naming_style.pascal_case.capitalization = pascal_case
43 |
44 | dotnet_naming_style.pascal_case.required_prefix =
45 | dotnet_naming_style.pascal_case.required_suffix =
46 | dotnet_naming_style.pascal_case.word_separator =
47 | dotnet_naming_style.pascal_case.capitalization = pascal_case
48 | dotnet_style_operator_placement_when_wrapping = beginning_of_line
49 | tab_width = 4
50 | indent_size = 4
51 | end_of_line = crlf
52 | dotnet_style_coalesce_expression = true:suggestion
53 | dotnet_style_null_propagation = true:suggestion
54 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
55 | dotnet_style_prefer_auto_properties = true:silent
56 | dotnet_style_object_initializer = true:suggestion
57 | dotnet_style_collection_initializer = true:suggestion
58 | dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
59 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent
60 | dotnet_style_prefer_conditional_expression_over_return = true:silent
61 | dotnet_style_explicit_tuple_names = true:suggestion
62 | dotnet_style_prefer_inferred_tuple_names = true:suggestion
63 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
64 | dotnet_style_prefer_compound_assignment = true:suggestion
65 | dotnet_style_prefer_simplified_interpolation = true:suggestion
66 | dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion
67 | dotnet_style_namespace_match_folder = true:suggestion
68 |
69 | [*.cs]
70 | csharp_indent_labels = no_change
71 | csharp_space_around_binary_operators = before_and_after
72 | csharp_using_directive_placement = outside_namespace:silent
73 | csharp_prefer_simple_using_statement = true:suggestion
74 | csharp_prefer_braces = true:silent
75 | csharp_style_namespace_declarations = block_scoped:silent
76 | csharp_style_prefer_method_group_conversion = true:silent
77 | csharp_style_prefer_top_level_statements = true:silent
78 | csharp_style_prefer_primary_constructors = true:suggestion
79 | csharp_prefer_system_threading_lock = true:suggestion
80 | csharp_style_expression_bodied_methods = false:silent
81 | csharp_style_expression_bodied_constructors = false:silent
82 | csharp_style_expression_bodied_operators = false:silent
83 | csharp_style_expression_bodied_properties = true:silent
84 | csharp_style_expression_bodied_indexers = true:silent
85 | csharp_style_expression_bodied_accessors = true:silent
86 | csharp_style_expression_bodied_lambdas = true:silent
87 | csharp_style_expression_bodied_local_functions = false:silent
88 | csharp_style_throw_expression = true:suggestion
89 | csharp_style_prefer_null_check_over_type_check = true:suggestion
90 | csharp_prefer_simple_default_expression = true:suggestion
91 | csharp_style_prefer_local_over_anonymous_function = true:suggestion
92 | csharp_style_prefer_index_operator = true:suggestion
93 | csharp_style_prefer_range_operator = true:suggestion
94 | csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
95 | csharp_style_prefer_implicitly_typed_lambda_expression = true:suggestion
96 | csharp_style_prefer_tuple_swap = true:suggestion
97 | csharp_style_prefer_unbound_generic_type_in_nameof = true:suggestion
98 | csharp_style_prefer_utf8_string_literals = true:suggestion
99 | csharp_style_inlined_variable_declaration = true:suggestion
100 | csharp_new_line_before_open_brace = none
101 | csharp_new_line_before_else = false
102 | csharp_new_line_before_catch = false
103 | csharp_new_line_before_finally = false
104 |
--------------------------------------------------------------------------------
/XenDriverUtils/InfFile.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Win32.SafeHandles;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.ComponentModel;
5 | using System.Runtime.InteropServices;
6 | using Windows.Win32;
7 | using Windows.Win32.Devices.DeviceAndDriverInstallation;
8 | using Windows.Win32.Foundation;
9 |
10 | namespace XenDriverUtils {
11 | public class InfFile : SafeHandleMinusOneIsInvalid {
12 | public InfFile(IntPtr existingHandle, bool ownsHandle) : base(ownsHandle) {
13 | handle = existingHandle;
14 | }
15 |
16 | public static InfFile Open(string FileName, string InfClass, INF_STYLE InfStyle, out uint ErrorLine) {
17 | unsafe {
18 | var infHandle = (IntPtr)PInvoke.SetupOpenInfFile(FileName, InfClass, InfStyle, out ErrorLine);
19 | if (infHandle == HANDLE.INVALID_HANDLE_VALUE) {
20 | var err = Marshal.GetLastWin32Error();
21 | throw new Win32Exception(err, $"SetupOpenInfFile {err}");
22 | }
23 | return new InfFile(infHandle, true);
24 | }
25 | }
26 |
27 | private INFCONTEXT FindFirstLine(string section, string key) {
28 | if (string.IsNullOrEmpty(section)) {
29 | throw new ArgumentNullException("Need valid section name");
30 | }
31 | unsafe {
32 | if (!PInvoke.SetupFindFirstLine(handle.ToPointer(), section, key, out var context)) {
33 | var err = Marshal.GetLastWin32Error();
34 | throw new Win32Exception(err, $"Inf cannot find line {section}/{key} {err}");
35 | }
36 | return context;
37 | }
38 | }
39 |
40 | private INFCONTEXT FindNextLine(INFCONTEXT contextIn) {
41 | unsafe {
42 | if (!PInvoke.SetupFindNextLine(contextIn, out var contextOut)) {
43 | var err = Marshal.GetLastWin32Error();
44 | throw new Win32Exception(err, $"Inf cannot find next line {err}");
45 | }
46 | return contextOut;
47 | }
48 | }
49 |
50 | private string GetLineText(ref INFCONTEXT context) {
51 | unsafe {
52 | fixed (INFCONTEXT* pContext = &context) {
53 | uint requiredChars;
54 | if (!PInvoke.SetupGetLineText(pContext, null, null, null, null, 0, &requiredChars)) {
55 | var err = Marshal.GetLastWin32Error();
56 | throw new Win32Exception(err, $"Inf cannot get line size {err}");
57 | }
58 | var mem = Marshal.AllocHGlobal((int)requiredChars * sizeof(char));
59 | try {
60 | if (!PInvoke.SetupGetLineText(
61 | pContext,
62 | null,
63 | null,
64 | null,
65 | new PWSTR((char*)mem.ToPointer()),
66 | requiredChars,
67 | null)) {
68 | var err = Marshal.GetLastWin32Error();
69 | throw new Win32Exception(err, $"Inf cannot get line {err}");
70 | }
71 | return Marshal.PtrToStringUni(mem);
72 | } finally {
73 | Marshal.FreeHGlobal(mem);
74 | }
75 | }
76 | }
77 | }
78 |
79 | private string GetStringField(ref INFCONTEXT context, uint fieldIndex) {
80 | unsafe {
81 | fixed (INFCONTEXT* pContext = &context) {
82 | uint requiredSize;
83 | if (!PInvoke.SetupGetStringField(pContext, fieldIndex, null, 0, &requiredSize)) {
84 | var err = Marshal.GetLastWin32Error();
85 | throw new Win32Exception(err, $"Inf cannot get field size {err}");
86 | }
87 | var mem = Marshal.AllocHGlobal((int)requiredSize * sizeof(char));
88 | try {
89 | if (!PInvoke.SetupGetStringField(
90 | pContext,
91 | fieldIndex,
92 | new PWSTR((char*)mem.ToPointer()),
93 | requiredSize,
94 | null)) {
95 | var err = Marshal.GetLastWin32Error();
96 | throw new Win32Exception(err, $"Inf cannot get field {err}");
97 | }
98 | return Marshal.PtrToStringUni(mem);
99 | } finally {
100 | Marshal.FreeHGlobal(mem);
101 | }
102 | }
103 | }
104 | }
105 |
106 | public string GetStringField(string section, string key, uint fieldIndex) {
107 | try {
108 | var line = FindFirstLine(section, key);
109 | return GetStringField(ref line, fieldIndex);
110 | } catch (Exception ex) {
111 | throw new KeyNotFoundException($"{section}/{key}", ex);
112 | }
113 | }
114 |
115 | protected override bool ReleaseHandle() {
116 | if (!IsInvalid) {
117 | unsafe {
118 | PInvoke.SetupCloseInfFile(handle.ToPointer());
119 | }
120 | }
121 | return true;
122 | }
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/.github/workflows/build-installer.yml:
--------------------------------------------------------------------------------
1 | name: build-installer
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | build-drivers:
8 | uses: ./.github/workflows/build-drivers.yml
9 | permissions:
10 | id-token: write
11 | contents: read
12 | attestations: write
13 | secrets: inherit
14 |
15 | build-guestagent:
16 | uses: ./.github/workflows/build-guestagent.yml
17 | permissions:
18 | id-token: write
19 | contents: read
20 | attestations: write
21 | secrets: inherit
22 |
23 | build-timeprovider:
24 | uses: ./.github/workflows/build-timeprovider.yml
25 | permissions:
26 | id-token: write
27 | contents: read
28 | attestations: write
29 | secrets: inherit
30 |
31 | build-installer:
32 | needs:
33 | - build-drivers
34 | - build-guestagent
35 | - build-timeprovider
36 | runs-on: windows-latest
37 | steps:
38 | - name: Checkout
39 | uses: actions/checkout@v4
40 | - name: Add SBOM tools
41 | run: |
42 | New-Item -Type Directory -Path $Env:RUNNER_TEMP/tools/
43 |
44 | Invoke-WebRequest -Uri https://github.com/microsoft/sbom-tool/releases/download/v4.1.4/sbom-tool-win-x64.exe -OutFile $Env:RUNNER_TEMP\tools\sbom.exe
45 | if ((Get-FileHash $Env:RUNNER_TEMP\tools\sbom.exe -Algorithm SHA256).Hash -ine "9ba208cbec21c93b6d1ba01f50681a47d9d14d302f59b33e505b220b89aa0363") {
46 | throw "sbom-tool failed verification"
47 | }
48 |
49 | Invoke-WebRequest -Uri https://github.com/anchore/syft/releases/download/v1.38.0/syft_1.38.0_windows_amd64.zip -OutFile $Env:RUNNER_TEMP\tools\syft.zip
50 | if ((Get-FileHash $Env:RUNNER_TEMP\tools\syft.zip -Algorithm SHA256).Hash -ine "11a3de4c82b001dae3b69e336f1b89391b676b3e74cead73790c97ff57445941") {
51 | throw "syft failed verification"
52 | }
53 | Expand-Archive $Env:RUNNER_TEMP\tools\syft.zip -DestinationPath $Env:RUNNER_TEMP\tools\syft -Force
54 | Copy-Item $Env:RUNNER_TEMP\tools\syft\syft.exe $Env:RUNNER_TEMP\tools\syft.exe -Force
55 |
56 | Add-Content -Path $Env:GITHUB_PATH -Value "$Env:RUNNER_TEMP/tools" -Force
57 | - name: Download Wix
58 | run: |
59 | $ErrorActionPreference = 'Stop'
60 | Remove-Item -Path .\deps -Recurse -Force -ErrorAction SilentlyContinue
61 | Invoke-WebRequest -Uri $Env:WIX_ZIP_URI -OutFile .\wix.zip
62 | $wixZipHash = (Get-FileHash .\wix.zip).Hash
63 | if ($wixZipHash -ine $Env:WIX_ZIP_SHA256) {
64 | throw "wix.zip failed verification"
65 | }
66 | Add-Content -Path $Env:GITHUB_STEP_SUMMARY -Value "wix.zip: ``$wixZipHash``" -Force
67 | Expand-Archive -Path .\wix.zip -DestinationPath .\deps\wix\ -Force
68 | env:
69 | # Dependency download links hardcoded for transparency.
70 | WIX_ZIP_URI: https://github.com/xcp-ng/wix-builder/releases/download/v6.0.1-xcpng.0/artifacts.zip
71 | WIX_ZIP_SHA256: 4d93cd40e778c1b9bebee7b0f2b1232c8e87f470a50eac212e2ed3bebad6e5b8
72 | - name: Download drivers
73 | uses: actions/download-artifact@v6
74 | with:
75 | name: drivers-signed
76 | path: installer/output/
77 | - name: Download guest agent
78 | uses: actions/download-artifact@v6
79 | with:
80 | name: guestagent-signed
81 | path: xen-guest-agent/target/release/
82 | - name: Download time provider
83 | uses: actions/download-artifact@v6
84 | with:
85 | name: timeprovider-signed
86 | path: xentimeprovider/x64/Release/
87 | - name: Add MSBuild
88 | uses: microsoft/setup-msbuild@v2
89 | - name: Configure branding
90 | run: .\scripts\branding-ci.ps1 -AddSigner -OutFile .\branding.ps1
91 | env:
92 | VENDOR_NAME: ${{vars.VENDOR_NAME}}
93 | PRODUCT_NAME: ${{vars.PRODUCT_NAME}}
94 | VENDOR_PREFIX: ${{vars.VENDOR_PREFIX}}
95 | COPYRIGHT: ${{vars.COPYRIGHT}}
96 | PackageVersions_Product: ${{vars.PackageVersions_Product}}.${{github.run_number}}
97 | PackageVersions_xenbus: ${{vars.PackageVersions_xenbus}}.${{github.run_number}}
98 | PackageVersions_xencons: ${{vars.PackageVersions_xencons}}.${{github.run_number}}
99 | PackageVersions_xenhid: ${{vars.PackageVersions_xenhid}}.${{github.run_number}}
100 | PackageVersions_xeniface: ${{vars.PackageVersions_xeniface}}.${{github.run_number}}
101 | PackageVersions_xennet: ${{vars.PackageVersions_xennet}}.${{github.run_number}}
102 | PackageVersions_xenvbd: ${{vars.PackageVersions_xenvbd}}.${{github.run_number}}
103 | PackageVersions_xenvif: ${{vars.PackageVersions_xenvif}}.${{github.run_number}}
104 | PackageVersions_xenvkbd: ${{vars.PackageVersions_xenvkbd}}.${{github.run_number}}
105 | PackageVersions_XenClean: ${{vars.PackageVersions_XenClean}}.${{github.run_number}}
106 | PackageVersions_XenBootFix: ${{vars.PackageVersions_XenBootFix}}.${{github.run_number}}
107 | PackageVersions_XenGuestAgent: ${{vars.PackageVersions_XenGuestAgent}}.${{github.run_number}}
108 | PackageVersions_XenTimeProvider: ${{vars.PackageVersions_XenTimeProvider}}.${{github.run_number}}
109 | MSI_UPGRADE_CODE_X86: ${{vars.MSI_UPGRADE_CODE_X86}}
110 | MSI_UPGRADE_CODE_X64: ${{vars.MSI_UPGRADE_CODE_X64}}
111 | # Signer cert must be injected here since unlike the drivers/guestagent,
112 | # we can't externally sign the installer package
113 | SIGNER_PFX_BASE64: ${{secrets.SIGNER_PFX_BASE64}}
114 | - name: Build installer
115 | id: build
116 | # this workflow is always testsigned, so -ExportCertificate is appropriate
117 | run: .\build-installer.ps1 -Configuration Release -Platform x64 -ExportSymbols -ExportExtras -ExportCertificate -Sbom
118 | - name: Clean up certificates
119 | if: always()
120 | run: Remove-Item Cert:\CurrentUser\My\* -ErrorAction SilentlyContinue
121 | - name: Upload installer
122 | id: upload
123 | uses: actions/upload-artifact@v5
124 | with:
125 | path: output/
126 | name: installer
127 | - name: Upload XenClean
128 | uses: actions/upload-artifact@v5
129 | with:
130 | path: output/${{steps.build.outputs.ReleaseTag}}/package/XenClean/
131 | name: XenClean
132 | outputs:
133 | subject-name: installer
134 | subject-digest: sha256:${{steps.upload.outputs.artifact-digest}}
135 |
136 | attest-installer:
137 | needs: [build-installer]
138 | uses: ./.github/workflows/attest.yml
139 | permissions:
140 | id-token: write
141 | contents: read
142 | attestations: write
143 | with:
144 | subject-name: ${{needs.build-installer.outputs.subject-name}}
145 | subject-digest: ${{needs.build-installer.outputs.subject-digest}}
146 |
--------------------------------------------------------------------------------
/installer/installer.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.11.35312.102
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{B7DD6F7E-DEF8-4E67-B5B7-07EF123DB6F0}") = "XenDrivers", "XenDrivers.wixproj", "{5EBD6640-0CBA-435D-BEBC-93182D18213D}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DriverInstallCustomAction", "..\DriverInstallCustomAction\DriverInstallCustomAction.csproj", "{EF921460-97E4-4EF0-92D5-BD9ABA124022}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XenDriverUtils", "..\XenDriverUtils\XenDriverUtils.csproj", "{1118970F-0929-4D5F-BDB2-54B2EDF42EF6}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XenClean", "..\XenClean\XenClean.csproj", "{0A953795-5150-4B83-85F9-FB906C8A6297}"
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{84018E1C-644B-466C-9788-939A31987B36}"
15 | ProjectSection(SolutionItems) = preProject
16 | ..\branding.ps1 = ..\branding.ps1
17 | EndProjectSection
18 | EndProject
19 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XenBootFix", "..\XenBootFix\XenBootFix.vcxproj", "{018433AB-F981-4625-8E23-6B3F22B7E5CF}"
20 | EndProject
21 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SBUpdate", "..\SBUpdate\SBUpdate.vcxproj", "{0972C789-7FA6-4ECC-A40B-71192B1BBF29}"
22 | EndProject
23 | Global
24 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
25 | Debug|ARM64 = Debug|ARM64
26 | Debug|x64 = Debug|x64
27 | Debug|x86 = Debug|x86
28 | Release|ARM64 = Release|ARM64
29 | Release|x64 = Release|x64
30 | Release|x86 = Release|x86
31 | EndGlobalSection
32 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
33 | {5EBD6640-0CBA-435D-BEBC-93182D18213D}.Debug|ARM64.ActiveCfg = Debug|ARM64
34 | {5EBD6640-0CBA-435D-BEBC-93182D18213D}.Debug|ARM64.Build.0 = Debug|ARM64
35 | {5EBD6640-0CBA-435D-BEBC-93182D18213D}.Debug|x64.ActiveCfg = Debug|x64
36 | {5EBD6640-0CBA-435D-BEBC-93182D18213D}.Debug|x64.Build.0 = Debug|x64
37 | {5EBD6640-0CBA-435D-BEBC-93182D18213D}.Debug|x86.ActiveCfg = Debug|x86
38 | {5EBD6640-0CBA-435D-BEBC-93182D18213D}.Debug|x86.Build.0 = Debug|x86
39 | {5EBD6640-0CBA-435D-BEBC-93182D18213D}.Release|ARM64.ActiveCfg = Release|ARM64
40 | {5EBD6640-0CBA-435D-BEBC-93182D18213D}.Release|ARM64.Build.0 = Release|ARM64
41 | {5EBD6640-0CBA-435D-BEBC-93182D18213D}.Release|x64.ActiveCfg = Release|x64
42 | {5EBD6640-0CBA-435D-BEBC-93182D18213D}.Release|x64.Build.0 = Release|x64
43 | {5EBD6640-0CBA-435D-BEBC-93182D18213D}.Release|x86.ActiveCfg = Release|x86
44 | {5EBD6640-0CBA-435D-BEBC-93182D18213D}.Release|x86.Build.0 = Release|x86
45 | {EF921460-97E4-4EF0-92D5-BD9ABA124022}.Debug|ARM64.ActiveCfg = Debug|ARM64
46 | {EF921460-97E4-4EF0-92D5-BD9ABA124022}.Debug|ARM64.Build.0 = Debug|ARM64
47 | {EF921460-97E4-4EF0-92D5-BD9ABA124022}.Debug|x64.ActiveCfg = Debug|x64
48 | {EF921460-97E4-4EF0-92D5-BD9ABA124022}.Debug|x64.Build.0 = Debug|x64
49 | {EF921460-97E4-4EF0-92D5-BD9ABA124022}.Debug|x86.ActiveCfg = Debug|x86
50 | {EF921460-97E4-4EF0-92D5-BD9ABA124022}.Debug|x86.Build.0 = Debug|x86
51 | {EF921460-97E4-4EF0-92D5-BD9ABA124022}.Release|ARM64.ActiveCfg = Release|ARM64
52 | {EF921460-97E4-4EF0-92D5-BD9ABA124022}.Release|ARM64.Build.0 = Release|ARM64
53 | {EF921460-97E4-4EF0-92D5-BD9ABA124022}.Release|x64.ActiveCfg = Release|x64
54 | {EF921460-97E4-4EF0-92D5-BD9ABA124022}.Release|x64.Build.0 = Release|x64
55 | {EF921460-97E4-4EF0-92D5-BD9ABA124022}.Release|x86.ActiveCfg = Release|x86
56 | {EF921460-97E4-4EF0-92D5-BD9ABA124022}.Release|x86.Build.0 = Release|x86
57 | {1118970F-0929-4D5F-BDB2-54B2EDF42EF6}.Debug|ARM64.ActiveCfg = Debug|ARM64
58 | {1118970F-0929-4D5F-BDB2-54B2EDF42EF6}.Debug|ARM64.Build.0 = Debug|ARM64
59 | {1118970F-0929-4D5F-BDB2-54B2EDF42EF6}.Debug|x64.ActiveCfg = Debug|x64
60 | {1118970F-0929-4D5F-BDB2-54B2EDF42EF6}.Debug|x64.Build.0 = Debug|x64
61 | {1118970F-0929-4D5F-BDB2-54B2EDF42EF6}.Debug|x86.ActiveCfg = Debug|x86
62 | {1118970F-0929-4D5F-BDB2-54B2EDF42EF6}.Debug|x86.Build.0 = Debug|x86
63 | {1118970F-0929-4D5F-BDB2-54B2EDF42EF6}.Release|ARM64.ActiveCfg = Release|ARM64
64 | {1118970F-0929-4D5F-BDB2-54B2EDF42EF6}.Release|ARM64.Build.0 = Release|ARM64
65 | {1118970F-0929-4D5F-BDB2-54B2EDF42EF6}.Release|x64.ActiveCfg = Release|x64
66 | {1118970F-0929-4D5F-BDB2-54B2EDF42EF6}.Release|x64.Build.0 = Release|x64
67 | {1118970F-0929-4D5F-BDB2-54B2EDF42EF6}.Release|x86.ActiveCfg = Release|x86
68 | {1118970F-0929-4D5F-BDB2-54B2EDF42EF6}.Release|x86.Build.0 = Release|x86
69 | {0A953795-5150-4B83-85F9-FB906C8A6297}.Debug|ARM64.ActiveCfg = Debug|ARM64
70 | {0A953795-5150-4B83-85F9-FB906C8A6297}.Debug|ARM64.Build.0 = Debug|ARM64
71 | {0A953795-5150-4B83-85F9-FB906C8A6297}.Debug|x64.ActiveCfg = Debug|x64
72 | {0A953795-5150-4B83-85F9-FB906C8A6297}.Debug|x64.Build.0 = Debug|x64
73 | {0A953795-5150-4B83-85F9-FB906C8A6297}.Debug|x86.ActiveCfg = Debug|x86
74 | {0A953795-5150-4B83-85F9-FB906C8A6297}.Debug|x86.Build.0 = Debug|x86
75 | {0A953795-5150-4B83-85F9-FB906C8A6297}.Release|ARM64.ActiveCfg = Release|ARM64
76 | {0A953795-5150-4B83-85F9-FB906C8A6297}.Release|ARM64.Build.0 = Release|ARM64
77 | {0A953795-5150-4B83-85F9-FB906C8A6297}.Release|x64.ActiveCfg = Release|x64
78 | {0A953795-5150-4B83-85F9-FB906C8A6297}.Release|x64.Build.0 = Release|x64
79 | {0A953795-5150-4B83-85F9-FB906C8A6297}.Release|x86.ActiveCfg = Release|x86
80 | {0A953795-5150-4B83-85F9-FB906C8A6297}.Release|x86.Build.0 = Release|x86
81 | {018433AB-F981-4625-8E23-6B3F22B7E5CF}.Debug|ARM64.ActiveCfg = Debug|x64
82 | {018433AB-F981-4625-8E23-6B3F22B7E5CF}.Debug|ARM64.Build.0 = Debug|x64
83 | {018433AB-F981-4625-8E23-6B3F22B7E5CF}.Debug|x64.ActiveCfg = Debug|x64
84 | {018433AB-F981-4625-8E23-6B3F22B7E5CF}.Debug|x64.Build.0 = Debug|x64
85 | {018433AB-F981-4625-8E23-6B3F22B7E5CF}.Debug|x86.ActiveCfg = Debug|Win32
86 | {018433AB-F981-4625-8E23-6B3F22B7E5CF}.Debug|x86.Build.0 = Debug|Win32
87 | {018433AB-F981-4625-8E23-6B3F22B7E5CF}.Release|ARM64.ActiveCfg = Release|x64
88 | {018433AB-F981-4625-8E23-6B3F22B7E5CF}.Release|ARM64.Build.0 = Release|x64
89 | {018433AB-F981-4625-8E23-6B3F22B7E5CF}.Release|x64.ActiveCfg = Release|x64
90 | {018433AB-F981-4625-8E23-6B3F22B7E5CF}.Release|x64.Build.0 = Release|x64
91 | {018433AB-F981-4625-8E23-6B3F22B7E5CF}.Release|x86.ActiveCfg = Release|Win32
92 | {018433AB-F981-4625-8E23-6B3F22B7E5CF}.Release|x86.Build.0 = Release|Win32
93 | {0972C789-7FA6-4ECC-A40B-71192B1BBF29}.Debug|ARM64.ActiveCfg = Debug|x64
94 | {0972C789-7FA6-4ECC-A40B-71192B1BBF29}.Debug|x64.ActiveCfg = Debug|x64
95 | {0972C789-7FA6-4ECC-A40B-71192B1BBF29}.Debug|x86.ActiveCfg = Debug|Win32
96 | {0972C789-7FA6-4ECC-A40B-71192B1BBF29}.Release|ARM64.ActiveCfg = Release|x64
97 | {0972C789-7FA6-4ECC-A40B-71192B1BBF29}.Release|x64.ActiveCfg = Release|x64
98 | {0972C789-7FA6-4ECC-A40B-71192B1BBF29}.Release|x86.ActiveCfg = Release|Win32
99 | EndGlobalSection
100 | GlobalSection(SolutionProperties) = preSolution
101 | HideSolutionNode = FALSE
102 | EndGlobalSection
103 | GlobalSection(ExtensibilityGlobals) = postSolution
104 | SolutionGuid = {E3EC35C1-B06B-42BF-BAF1-4AF0840DBB1A}
105 | EndGlobalSection
106 | EndGlobal
107 |
--------------------------------------------------------------------------------
/DriverInstallCustomAction/DriverActions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading;
5 | using Windows.Win32;
6 | using Windows.Win32.Devices.DeviceAndDriverInstallation;
7 | using Windows.Win32.Foundation;
8 | using WixToolset.Dtf.WindowsInstaller;
9 | using XenDriverUtils;
10 |
11 | namespace XenInstCA {
12 | internal class DriverData {
13 | public string DriverName { get; set; }
14 | public string InfPath { get; set; }
15 | }
16 |
17 | public class DriverActions {
18 | private static DriverData GetDriverData(Session session) {
19 | if (!session.CustomActionData.TryGetValue("Driver", out var driverName)) return null;
20 | if (!session.CustomActionData.TryGetValue("Inf", out var infPath)) return null;
21 | Logger.Log($"driverName {driverName} infPath {infPath}");
22 | return new DriverData() {
23 | DriverName = driverName,
24 | InfPath = infPath,
25 | };
26 | }
27 |
28 | [CustomAction]
29 | public static ActionResult DriverInstall(Session session) {
30 | using var logScope = new LoggerScope(new MsiSessionLogger(session));
31 |
32 | var driver = GetDriverData(session);
33 | if (driver == null) {
34 | return ActionResult.Success;
35 | }
36 |
37 | if (CustomActionUtils.ReportAction(
38 | session,
39 | $"{driver.DriverName}Install",
40 | driver.DriverName) == MessageResult.Cancel) {
41 | return ActionResult.UserExit;
42 | }
43 | Logger.Log($"Installing {driver.DriverName} inf {driver.InfPath}");
44 |
45 | DriverUtils.InstallDriver(driver.InfPath, out var needsReboot);
46 | if (needsReboot) {
47 | CustomActionUtils.ScheduleReboot();
48 | }
49 | return ActionResult.Success;
50 | }
51 |
52 | [CustomAction]
53 | public static ActionResult DriverInstallRollback(Session session) {
54 | using var logScope = new LoggerScope(new MsiSessionLogger(session));
55 | DriverUninstall(session);
56 | return ActionResult.Success;
57 | }
58 |
59 | [CustomAction]
60 | public static ActionResult DriverUninstall(Session session) {
61 | using var logScope = new LoggerScope(new MsiSessionLogger(session));
62 |
63 | bool needsReboot = false;
64 | var driver = GetDriverData(session);
65 | if (driver == null) {
66 | return ActionResult.Success;
67 | }
68 |
69 | if (CustomActionUtils.ReportAction(
70 | session,
71 | $"{driver.DriverName}Uninstall",
72 | driver.DriverName) == MessageResult.Cancel) {
73 | return ActionResult.UserExit;
74 | }
75 |
76 | Logger.Log($"Uninstalling {driver.DriverName} inf {driver.InfPath}");
77 | if (!XenDeviceInfo.KnownDevices.TryGetValue(driver.DriverName, out var xenInfo)) {
78 | Logger.Log($"Unknown driver {driver.DriverName}");
79 | return ActionResult.Success;
80 | }
81 |
82 | var devInfo = PInvoke.SetupDiGetClassDevs(
83 | xenInfo.ClassGuid,
84 | null,
85 | HWND.Null,
86 | xenInfo.ClassGuid.HasValue ? 0 : SETUP_DI_GET_CLASS_DEVS_FLAGS.DIGCF_ALLCLASSES);
87 | var collectedInfPaths = new HashSet(StringComparer.OrdinalIgnoreCase);
88 |
89 | foreach (var devInfoData in DriverUtils.EnumerateDevices(devInfo)) {
90 | List hardwareIds = DriverUtils.GetDeviceHardwareAndCompatibleIds(devInfo, devInfoData);
91 | if (hardwareIds.All(x => !xenInfo.MatchesId(x, checkKnown: true, checkIncompatible: false))) {
92 | continue;
93 | }
94 |
95 | var instanceId = DriverUtils.GetDeviceInstanceId(devInfo, devInfoData);
96 | if (instanceId != null) {
97 | Logger.Log($"Found {driver.DriverName} device: {instanceId}");
98 | } else {
99 | Logger.Log($"Found {driver.DriverName} device");
100 | }
101 |
102 | var infName = DriverUtils.GetDeviceDriverInfPath(devInfo, devInfoData);
103 | Logger.Log($"Current inf path: {infName}");
104 | if (!string.IsNullOrEmpty(infName)
105 | && infName.StartsWith("oem", StringComparison.OrdinalIgnoreCase)) {
106 | collectedInfPaths.Add(infName);
107 | }
108 |
109 | try {
110 | DriverUtils.UninstallDevice(devInfo, devInfoData, out var thisNeedsReboot);
111 | needsReboot |= thisNeedsReboot;
112 | } catch (Exception ex) {
113 | Logger.Log($"Cannot uninstall device: {ex.Message}");
114 | }
115 | }
116 |
117 | if (collectedInfPaths.Count > 0) {
118 | foreach (var oemInfName in collectedInfPaths) {
119 | try {
120 | DriverUtils.UninstallDriver(oemInfName);
121 | } catch (Exception ex) {
122 | Logger.Log($"Cannot uninstall driver {oemInfName}: {ex.Message}");
123 | }
124 | }
125 | }
126 | // Why uninstall everything during DriverInstall?
127 | // Some older drivers (e.g. old XCP-ng drivers) don't like it when downgraded from a newer version.
128 | // Remove them all just to be sure.
129 | // We should arguably require running XenClean first but this covers cases where older drivers are installed
130 | // after ours.
131 | DriverUtils.UninstallDriverByNames(driver.DriverName);
132 |
133 | if (needsReboot) {
134 | CustomActionUtils.ScheduleReboot();
135 | }
136 | return ActionResult.Success;
137 | }
138 |
139 | [CustomAction]
140 | public static ActionResult DriverUninstallRollback(Session session) {
141 | using var logScope = new LoggerScope(new MsiSessionLogger(session));
142 | DriverInstall(session);
143 | return ActionResult.Success;
144 | }
145 |
146 | [CustomAction]
147 | public static ActionResult DriverWaitInstallFinish(Session session) {
148 | using var logScope = new LoggerScope(new MsiSessionLogger(session));
149 | CustomActionUtils.ReportAction(session, $"XenWaitDriverInstall", "");
150 | // wait for up to 1 minute until all pending installations are done
151 | for (int i = 0; i < 5; i++) {
152 | // wait twice just to be sure that Windows doesn't decide to install something a second time
153 | DriverUtils.WaitNoPendingInstallEvents(5000);
154 | Thread.Sleep(2000);
155 | if (DriverUtils.WaitNoPendingInstallEvents(5000)) {
156 | break;
157 | }
158 | }
159 | return ActionResult.Success;
160 | }
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Oo]ut/
33 | [Ll]og/
34 | [Ll]ogs/
35 |
36 | # Visual Studio 2015/2017 cache/options directory
37 | .vs/
38 | # Uncomment if you have tasks that create the project's static files in wwwroot
39 | #wwwroot/
40 |
41 | # Visual Studio 2017 auto generated files
42 | Generated\ Files/
43 |
44 | # MSTest test Results
45 | [Tt]est[Rr]esult*/
46 | [Bb]uild[Ll]og.*
47 |
48 | # NUnit
49 | *.VisualState.xml
50 | TestResult.xml
51 | nunit-*.xml
52 |
53 | # Build Results of an ATL Project
54 | [Dd]ebugPS/
55 | [Rr]eleasePS/
56 | dlldata.c
57 |
58 | # Benchmark Results
59 | BenchmarkDotNet.Artifacts/
60 |
61 | # .NET Core
62 | project.lock.json
63 | project.fragment.lock.json
64 | artifacts/
65 |
66 | # ASP.NET Scaffolding
67 | ScaffoldingReadMe.txt
68 |
69 | # StyleCop
70 | StyleCopReport.xml
71 |
72 | # Files built by Visual Studio
73 | *_i.c
74 | *_p.c
75 | *_h.h
76 | *.ilk
77 | *.meta
78 | *.obj
79 | *.iobj
80 | *.pch
81 | *.pdb
82 | *.ipdb
83 | *.pgc
84 | *.pgd
85 | *.rsp
86 | *.sbr
87 | *.tlb
88 | *.tli
89 | *.tlh
90 | *.tmp
91 | *.tmp_proj
92 | *_wpftmp.csproj
93 | *.log
94 | *.vspscc
95 | *.vssscc
96 | .builds
97 | *.pidb
98 | *.svclog
99 | *.scc
100 |
101 | # Chutzpah Test files
102 | _Chutzpah*
103 |
104 | # Visual C++ cache files
105 | ipch/
106 | *.aps
107 | *.ncb
108 | *.opendb
109 | *.opensdf
110 | *.sdf
111 | *.cachefile
112 | *.VC.db
113 | *.VC.VC.opendb
114 |
115 | # Visual Studio profiler
116 | *.psess
117 | *.vsp
118 | *.vspx
119 | *.sap
120 |
121 | # Visual Studio Trace Files
122 | *.e2e
123 |
124 | # TFS 2012 Local Workspace
125 | $tf/
126 |
127 | # Guidance Automation Toolkit
128 | *.gpState
129 |
130 | # ReSharper is a .NET coding add-in
131 | _ReSharper*/
132 | *.[Rr]e[Ss]harper
133 | *.DotSettings.user
134 |
135 | # TeamCity is a build add-in
136 | _TeamCity*
137 |
138 | # DotCover is a Code Coverage Tool
139 | *.dotCover
140 |
141 | # AxoCover is a Code Coverage Tool
142 | .axoCover/*
143 | !.axoCover/settings.json
144 |
145 | # Coverlet is a free, cross platform Code Coverage Tool
146 | coverage*.json
147 | coverage*.xml
148 | coverage*.info
149 |
150 | # Visual Studio code coverage results
151 | *.coverage
152 | *.coveragexml
153 |
154 | # NCrunch
155 | _NCrunch_*
156 | .*crunch*.local.xml
157 | nCrunchTemp_*
158 |
159 | # MightyMoose
160 | *.mm.*
161 | AutoTest.Net/
162 |
163 | # Web workbench (sass)
164 | .sass-cache/
165 |
166 | # Installshield output folder
167 | [Ee]xpress/
168 |
169 | # DocProject is a documentation generator add-in
170 | DocProject/buildhelp/
171 | DocProject/Help/*.HxT
172 | DocProject/Help/*.HxC
173 | DocProject/Help/*.hhc
174 | DocProject/Help/*.hhk
175 | DocProject/Help/*.hhp
176 | DocProject/Help/Html2
177 | DocProject/Help/html
178 |
179 | # Click-Once directory
180 | publish/
181 |
182 | # Publish Web Output
183 | *.[Pp]ublish.xml
184 | *.azurePubxml
185 | # Note: Comment the next line if you want to checkin your web deploy settings,
186 | # but database connection strings (with potential passwords) will be unencrypted
187 | *.pubxml
188 | *.publishproj
189 |
190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
191 | # checkin your Azure Web App publish settings, but sensitive information contained
192 | # in these scripts will be unencrypted
193 | PublishScripts/
194 |
195 | # NuGet Packages
196 | *.nupkg
197 | # NuGet Symbol Packages
198 | *.snupkg
199 | # The packages folder can be ignored because of Package Restore
200 | **/[Pp]ackages/*
201 | # except build/, which is used as an MSBuild target.
202 | !**/[Pp]ackages/build/
203 | # Uncomment if necessary however generally it will be regenerated when needed
204 | #!**/[Pp]ackages/repositories.config
205 | # NuGet v3's project.json files produces more ignorable files
206 | *.nuget.props
207 | *.nuget.targets
208 |
209 | # Microsoft Azure Build Output
210 | csx/
211 | *.build.csdef
212 |
213 | # Microsoft Azure Emulator
214 | ecf/
215 | rcf/
216 |
217 | # Windows Store app package directories and files
218 | AppPackages/
219 | BundleArtifacts/
220 | Package.StoreAssociation.xml
221 | _pkginfo.txt
222 | *.appx
223 | *.appxbundle
224 | *.appxupload
225 |
226 | # Visual Studio cache files
227 | # files ending in .cache can be ignored
228 | *.[Cc]ache
229 | # but keep track of directories ending in .cache
230 | !?*.[Cc]ache/
231 |
232 | # Others
233 | ClientBin/
234 | ~$*
235 | *~
236 | *.dbmdl
237 | *.dbproj.schemaview
238 | *.jfm
239 | *.pfx
240 | *.publishsettings
241 | orleans.codegen.cs
242 |
243 | # Including strong name files can present a security risk
244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
245 | #*.snk
246 |
247 | # Since there are multiple workflows, uncomment next line to ignore bower_components
248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
249 | #bower_components/
250 |
251 | # RIA/Silverlight projects
252 | Generated_Code/
253 |
254 | # Backup & report files from converting an old project file
255 | # to a newer Visual Studio version. Backup files are not needed,
256 | # because we have git ;-)
257 | _UpgradeReport_Files/
258 | Backup*/
259 | UpgradeLog*.XML
260 | UpgradeLog*.htm
261 | ServiceFabricBackup/
262 | *.rptproj.bak
263 |
264 | # SQL Server files
265 | *.mdf
266 | *.ldf
267 | *.ndf
268 |
269 | # Business Intelligence projects
270 | *.rdl.data
271 | *.bim.layout
272 | *.bim_*.settings
273 | *.rptproj.rsuser
274 | *- [Bb]ackup.rdl
275 | *- [Bb]ackup ([0-9]).rdl
276 | *- [Bb]ackup ([0-9][0-9]).rdl
277 |
278 | # Microsoft Fakes
279 | FakesAssemblies/
280 |
281 | # GhostDoc plugin setting file
282 | *.GhostDoc.xml
283 |
284 | # Node.js Tools for Visual Studio
285 | .ntvs_analysis.dat
286 | node_modules/
287 |
288 | # Visual Studio 6 build log
289 | *.plg
290 |
291 | # Visual Studio 6 workspace options file
292 | *.opt
293 |
294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
295 | *.vbw
296 |
297 | # Visual Studio LightSwitch build output
298 | **/*.HTMLClient/GeneratedArtifacts
299 | **/*.DesktopClient/GeneratedArtifacts
300 | **/*.DesktopClient/ModelManifest.xml
301 | **/*.Server/GeneratedArtifacts
302 | **/*.Server/ModelManifest.xml
303 | _Pvt_Extensions
304 |
305 | # Paket dependency manager
306 | .paket/paket.exe
307 | paket-files/
308 |
309 | # FAKE - F# Make
310 | .fake/
311 |
312 | # CodeRush personal settings
313 | .cr/personal
314 |
315 | # Python Tools for Visual Studio (PTVS)
316 | __pycache__/
317 | *.pyc
318 |
319 | # Cake - Uncomment if you are using it
320 | # tools/**
321 | # !tools/packages.config
322 |
323 | # Tabs Studio
324 | *.tss
325 |
326 | # Telerik's JustMock configuration file
327 | *.jmconfig
328 |
329 | # BizTalk build output
330 | *.btp.cs
331 | *.btm.cs
332 | *.odx.cs
333 | *.xsd.cs
334 |
335 | # OpenCover UI analysis results
336 | OpenCover/
337 |
338 | # Azure Stream Analytics local run output
339 | ASALocalRun/
340 |
341 | # MSBuild Binary and Structured Log
342 | *.binlog
343 |
344 | # NVidia Nsight GPU debugger configuration file
345 | *.nvuser
346 |
347 | # MFractors (Xamarin productivity tool) working folder
348 | .mfractor/
349 |
350 | # Local History for Visual Studio
351 | .localhistory/
352 |
353 | # BeatPulse healthcheck temp database
354 | healthchecksdb
355 |
356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
357 | MigrationBackup/
358 |
359 | # Ionide (cross platform F# VS Code tools) working folder
360 | .ionide/
361 |
362 | # Fody - auto-generated XML schema
363 | FodyWeavers.xsd
364 |
365 | # CodeQL
366 | *.sarif
367 | *.dvl.xml
368 |
369 | .build_number
370 | /branding.ps1
371 | Branding.wxi
372 | Branding.cs
373 | /package/
374 | /XenBootFix/Branding.inc
375 | /package-*
376 | output/
377 | deps/
378 |
--------------------------------------------------------------------------------
/XenDriverUtils/XenCleanup.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Win32;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Runtime.InteropServices;
6 | using Windows.Win32;
7 | using Windows.Win32.Foundation;
8 |
9 | namespace XenDriverUtils {
10 | public class ThirdPartyStorageDriver {
11 | public string DriverInfPath { get; set; }
12 | public string Service { get; set; }
13 | }
14 |
15 | public class XenCleanup {
16 | static readonly IReadOnlyList StorageClasses = new List() {
17 | PInvoke.GUID_DEVCLASS_HDC,
18 | PInvoke.GUID_DEVCLASS_SCSIADAPTER,
19 | };
20 |
21 | public static List Find3PStorageDrivers() {
22 | var found3PDrivers = new List();
23 | foreach (var classGuid in StorageClasses) {
24 | var devInfo = PInvoke.SetupDiGetClassDevs(classGuid, null, HWND.Null, 0);
25 | foreach (var devInfoData in DriverUtils.EnumerateDevices(devInfo)
26 | .Where(x => DriverUtils.GetDeviceEnumeratorName(devInfo, x) == "PCI")) {
27 | var driverPath = DriverUtils.GetDeviceDriverInfPath(devInfo, devInfoData);
28 | if (driverPath.StartsWith("oem", StringComparison.OrdinalIgnoreCase)) {
29 | found3PDrivers.Add(new ThirdPartyStorageDriver() {
30 | DriverInfPath = driverPath,
31 | Service = DriverUtils.GetDeviceService(devInfo, devInfoData),
32 | });
33 | }
34 | }
35 | }
36 | return found3PDrivers;
37 | }
38 |
39 | private static readonly List XenfiltClasses = new() {
40 | PInvoke.GUID_DEVCLASS_HDC,
41 | PInvoke.GUID_DEVCLASS_SYSTEM,
42 | };
43 |
44 | private static readonly List FilterValueList = new() {
45 | "LowerFilters",
46 | "UpperFilters",
47 | };
48 |
49 | private static readonly List FilterNameList = new() {
50 | "xenfilt",
51 | "scsifilt",
52 | };
53 |
54 | public static void XenfiltClassCleanup() {
55 | using var classKey = Registry.LocalMachine.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\Class", true);
56 | if (classKey == null) {
57 | return;
58 | }
59 | foreach (var classGuid in XenfiltClasses) {
60 | using var classSubkey = classKey.OpenSubKey(classGuid.ToString("B"), true);
61 | if (classSubkey == null) {
62 | continue;
63 | }
64 | foreach (var filterValue in FilterValueList) {
65 | try {
66 | if (classSubkey.GetValueKind(filterValue) == RegistryValueKind.MultiString) {
67 | var filters = (string[])classSubkey.GetValue(filterValue);
68 | Logger.Log($"Class filters for {classGuid}: {string.Join(",", filters)}");
69 | var newFilters = filters.Where(x => !FilterNameList
70 | .Contains(x, StringComparer.OrdinalIgnoreCase))
71 | .ToArray();
72 | Logger.Log($"New filters for {classGuid}: {string.Join(",", newFilters)}");
73 | classSubkey.SetValue(filterValue, newFilters, RegistryValueKind.MultiString);
74 | }
75 | } catch {
76 | }
77 | }
78 | }
79 | }
80 |
81 | private static readonly List OverridesToDelete = new() {
82 | "stornvme",
83 | };
84 |
85 | public static void ResetStartOverride() {
86 | try {
87 | foreach (var overrideName in OverridesToDelete.Concat(Find3PStorageDrivers().Select(x => x.Service))) {
88 | Logger.Log($"Resetting {overrideName} StartOverride");
89 | Registry.LocalMachine.DeleteSubKey(
90 | $"SYSTEM\\CurrentControlSet\\Services\\{overrideName}\\StartOverride",
91 | false);
92 | }
93 | } catch (Exception ex) {
94 | Logger.Log($"Cannot delete StartOverride subkey: {ex.Message}");
95 | }
96 | }
97 |
98 | private static readonly List XenfiltParametersToDelete = new() {
99 | "ActiveDeviceID",
100 | "ActiveInstanceID",
101 | "ActiveLocationInformation",
102 | };
103 |
104 | public static void XenfiltReset() {
105 | using var paramKey = Registry.LocalMachine.OpenSubKey(
106 | "SYSTEM\\CurrentControlSet\\Services\\xenfilt\\Parameters",
107 | true);
108 | if (paramKey == null) {
109 | return;
110 | }
111 | foreach (var paramName in XenfiltParametersToDelete) {
112 | try {
113 | paramKey.DeleteValue(paramName);
114 | Logger.Log($"Deleted xenfilt parameter {paramName}");
115 | } catch {
116 | }
117 | }
118 | }
119 |
120 | public static void ResetUnplug() {
121 | try {
122 | using var key = Registry.LocalMachine.OpenSubKey("SYSTEM\\CurrentControlSet\\Services\\XEN", true);
123 | if (key == null) {
124 | return;
125 | }
126 | Logger.Log("Resetting Unplug key");
127 | key.DeleteSubKey("Unplug");
128 | } catch {
129 | }
130 | }
131 |
132 | public static void ResetForceUnplug() {
133 | try {
134 | using var key = Registry.LocalMachine.OpenSubKey("SYSTEM\\CurrentControlSet\\Services\\XEN", true);
135 | if (key == null) {
136 | return;
137 | }
138 | Logger.Log("Resetting Unplug key");
139 | key.DeleteSubKey("ForceUnplug");
140 | } catch {
141 | }
142 | }
143 |
144 | // (ServiceName, IsDriverService)
145 | // other code may want to use this list too, so make it public
146 | public static readonly List> DeleteableServices = new() {
147 | new("xenagent", true),
148 | new("xenbus", true),
149 | new("xenbus_monitor", true),
150 | new("xencons", true),
151 | new("xencons_monitor", true),
152 | new("xendisk", true),
153 | new("xenfilt", true),
154 | new("xenhid", true),
155 | new("xeniface", true),
156 | new("xennet", true),
157 | new("xenvbd", true),
158 | new("xenvif", true),
159 | new("xenvkbd", true),
160 | new("XenInstall", false),
161 | new("XenSvc", false),
162 | };
163 |
164 | public static void DeleteService(CloseServiceHandleSafeHandle scm, string serviceName, bool stop = true) {
165 | if (!DeleteableServices.Any(x => string.Equals(x.Item1, serviceName, StringComparison.OrdinalIgnoreCase))) {
166 | Logger.Log($"Refusing to delete service {serviceName}");
167 | return;
168 | }
169 | Logger.Log($"Deleting service {serviceName}");
170 |
171 | using var service = PInvoke.OpenService(scm, serviceName, PInvoke.SERVICE_ALL_ACCESS);
172 | if (service.IsInvalid) {
173 | Logger.Log($"OpenService {serviceName} error {Marshal.GetLastWin32Error()}");
174 | return;
175 | }
176 |
177 | if (stop) {
178 | if (PInvoke.ControlService(service, PInvoke.SERVICE_CONTROL_STOP, out var status)) {
179 | Logger.Log($"Service {serviceName} stopped");
180 | } else {
181 | Logger.Log($"ControlService({serviceName}, SERVICE_CONTROL_STOP) error {Marshal.GetLastWin32Error()}");
182 | }
183 | }
184 |
185 | if (PInvoke.DeleteService(service)) {
186 | Logger.Log($"Service {serviceName} deleted");
187 | } else {
188 | Logger.Log($"DeleteService({serviceName}) error {Marshal.GetLastWin32Error()}");
189 | }
190 | }
191 |
192 | public static bool IsSafeMode() {
193 | return !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("SAFEBOOT_OPTION"));
194 | }
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/XenDriverUtils/Copy-XenVifSettings.ps1:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: BSD-2-Clause
2 |
3 | # Attention, this script is branding-agnostic!
4 |
5 | #Requires -RunAsAdministrator
6 |
7 | [CmdletBinding(SupportsShouldProcess)]
8 | param(
9 | # Run the task now.
10 | [Parameter(Mandatory, ParameterSetName = "Invoke")]
11 | [switch]$Invoke,
12 | # Install scheduled task to run on reboot.
13 | [Parameter(Mandatory, ParameterSetName = "Install")]
14 | [switch]$Install,
15 |
16 | [Parameter()]
17 | [switch]$Backup,
18 | [Parameter()]
19 | [switch]$Restore,
20 |
21 | [Parameter()]
22 | [switch]$Paravirtualized,
23 | [Parameter()]
24 | [switch]$Emulated,
25 |
26 | # Delete script after running.
27 | [Parameter(ParameterSetName = "Invoke")]
28 | [switch]$SelfDestruct
29 | )
30 |
31 | $ErrorActionPreference = "Stop"
32 |
33 | if (![Environment]::Is64BitProcess) {
34 | throw "Cannot run this script from PowerShell x86!"
35 | }
36 |
37 | $Script:ScheduledTaskName = "Copy-XenVifSettings"
38 | $Script:InstallPath = "$env:ProgramFiles\Copy-XenVifSettings.ps1"
39 | $Script:PowershellPath = Join-Path ([System.Environment]::SystemDirectory) "WindowsPowerShell\v1.0\powershell.exe"
40 |
41 | function Backup-XenVifSettings {
42 | [CmdletBinding(SupportsShouldProcess)]
43 | param (
44 | [Parameter(Mandatory)][string]$InterfaceGuid,
45 | [Parameter(Mandatory)][string]$PermanentAddress
46 | )
47 |
48 | Write-Verbose "Backing up $PermanentAddress = $InterfaceGuid"
49 |
50 | Remove-Item -Path HKLM:\SOFTWARE\XenOffboard\Xenvif\$PermanentAddress\Tcpip -Force -Recurse -ErrorAction SilentlyContinue -WhatIf:$WhatIfPreference
51 | New-Item -Path HKLM:\SOFTWARE\XenOffboard\Xenvif\$PermanentAddress\Tcpip -Force -WhatIf:$WhatIfPreference
52 | Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\$InterfaceGuid -ErrorAction Continue | `
53 | Select-Object -Property * -ExcludeProperty PSPath, PSParentPath, PSChildName, PSDrive, PSProvider | `
54 | Set-ItemProperty -Path HKLM:\SOFTWARE\XenOffboard\Xenvif\$PermanentAddress\Tcpip -ErrorAction Continue -WhatIf:$WhatIfPreference
55 |
56 | Remove-Item -Path HKLM:\SOFTWARE\XenOffboard\Xenvif\$PermanentAddress\Tcpip6 -Force -Recurse -ErrorAction SilentlyContinue -WhatIf:$WhatIfPreference
57 | New-Item -Path HKLM:\SOFTWARE\XenOffboard\Xenvif\$PermanentAddress\Tcpip6 -Force -WhatIf:$WhatIfPreference
58 | Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\Interfaces\$InterfaceGuid -ErrorAction Continue | `
59 | Select-Object -Property * -ExcludeProperty PSPath, PSParentPath, PSChildName, PSDrive, PSProvider | `
60 | Set-ItemProperty -Path HKLM:\SOFTWARE\XenOffboard\Xenvif\$PermanentAddress\Tcpip6 -ErrorAction Continue -WhatIf:$WhatIfPreference
61 | }
62 |
63 | function Restore-XenVifSettings {
64 | [CmdletBinding(SupportsShouldProcess)]
65 | param (
66 | [Parameter(Mandatory)][string]$InterfaceGuid,
67 | [Parameter(Mandatory)][string]$PermanentAddress
68 | )
69 |
70 | $Restored = $false
71 |
72 | if (Test-Path HKLM:\SOFTWARE\XenOffboard\Xenvif\$PermanentAddress\Tcpip) {
73 | Write-Verbose "$PermanentAddress = $InterfaceGuid has Tcpip"
74 |
75 | Get-ItemProperty -Path HKLM:\SOFTWARE\XenOffboard\Xenvif\$PermanentAddress\Tcpip -ErrorAction Continue | `
76 | Select-Object -Property * -ExcludeProperty PSPath, PSParentPath, PSChildName, PSDrive, PSProvider | `
77 | Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\$InterfaceGuid -ErrorAction Continue -WhatIf:$WhatIfPreference
78 |
79 | $Restored = $true
80 | }
81 |
82 | if (Test-Path HKLM:\SOFTWARE\XenOffboard\Xenvif\$PermanentAddress\Tcpip6) {
83 | Write-Verbose "$PermanentAddress = $InterfaceGuid has Tcpip6"
84 |
85 | Get-ItemProperty -Path HKLM:\SOFTWARE\XenOffboard\Xenvif\$PermanentAddress\Tcpip6 -ErrorAction Continue | `
86 | Select-Object -Property * -ExcludeProperty PSPath, PSParentPath, PSChildName, PSDrive, PSProvider | `
87 | Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\Interfaces\$InterfaceGuid -ErrorAction Continue -WhatIf:$WhatIfPreference
88 |
89 | $Restored = $true
90 | }
91 |
92 | if ($Restored) {
93 | # reload settings from registry
94 | Get-NetAdapter | Where-Object InterfaceGuid -ieq $InterfaceGuid | `
95 | Disable-NetAdapter -Confirm:$false -PassThru -WhatIf:$WhatIfPreference | `
96 | Enable-NetAdapter -Confirm:$false -WhatIf:$WhatIfPreference
97 | }
98 | }
99 |
100 | if ($Backup -and $Restore) {
101 | throw "-Backup and -Restore are mutually exclusive"
102 | }
103 | elseif (!$Backup -and !$Restore) {
104 | throw "Must specify an action"
105 | }
106 |
107 | if ($Paravirtualized -and $Emulated) {
108 | throw "-Paravirtualized and -Emulated are mutually exclusive"
109 | }
110 | elseif (!$Paravirtualized -and !$Emulated) {
111 | throw "Must specify a device type"
112 | }
113 |
114 | if ($Invoke) {
115 | try {
116 | $MatchingInterfaces = if ($Paravirtualized) {
117 | Get-NetAdapter | Where-Object PnPDeviceID -like "XENVIF\*"
118 | }
119 | elseif ($Emulated) {
120 | Get-NetAdapter | Where-Object PnPDeviceID -like "PCI\*"
121 | }
122 |
123 | if ($Backup) {
124 | Remove-Item -Path HKLM:\SOFTWARE\XenOffboard\Xenvif -Force -Recurse -ErrorAction SilentlyContinue -WhatIf:$WhatIfPreference
125 | $MatchingInterfaces | ForEach-Object {
126 | if ($_.InterfaceGuid -ne $null -and $_.PermanentAddress -ne $null) {
127 | Backup-XenVifSettings -InterfaceGuid $_.InterfaceGuid -PermanentAddress $_.PermanentAddress -WhatIf:$WhatIfPreference
128 | }
129 | }
130 | }
131 | elseif ($Restore) {
132 | $MatchingInterfaces | ForEach-Object {
133 | if ($_.InterfaceGuid -ne $null -and $_.PermanentAddress -ne $null) {
134 | Restore-XenVifSettings -InterfaceGuid $_.InterfaceGuid -PermanentAddress $_.PermanentAddress -WhatIf:$WhatIfPreference
135 | }
136 | }
137 | }
138 | }
139 | finally {
140 | if ($SelfDestruct) {
141 | Get-ScheduledTask -TaskName $Script:ScheduledTaskName -ErrorAction SilentlyContinue | `
142 | Unregister-ScheduledTask -Confirm:$false -WhatIf:$WhatIfPreference -ErrorAction SilentlyContinue
143 | Remove-Item $PSCommandPath -Force -WhatIf:$WhatIfPreference
144 | }
145 | }
146 | }
147 |
148 | elseif ($Install) {
149 | Write-Verbose "Current path: $PSCommandPath"
150 | Write-Verbose "Install path: $Script:InstallPath"
151 |
152 | if ((Convert-Path $PSCommandPath -ErrorAction SilentlyContinue) -ieq (Convert-Path $Script:InstallPath -ErrorAction SilentlyContinue)) {
153 | throw "Cannot install from already-installed script, abandoning"
154 | }
155 |
156 | Copy-Item $PSCommandPath -Destination $Script:InstallPath -Force -WhatIf:$WhatIfPreference
157 |
158 | $existingTask = Get-ScheduledTask -TaskName $Script:ScheduledTaskName -ErrorAction SilentlyContinue
159 | if ($null -ne $existingTask) {
160 | Write-Verbose "Scheduled task is already installed, reinstalling"
161 | $existingTask | Unregister-ScheduledTask -Confirm:$false -WhatIf:$WhatIfPreference
162 | }
163 |
164 | $cmdArgs = @(
165 | "-Invoke",
166 | "-SelfDestruct"
167 | )
168 |
169 | if ($Backup) {
170 | $cmdArgs += @("-Backup")
171 | }
172 | elseif ($Restore) {
173 | $cmdArgs += @("-Restore")
174 | }
175 |
176 | if ($Paravirtualized) {
177 | $cmdArgs += @("-Paravirtualized")
178 | }
179 | elseif ($Emulated) {
180 | $cmdArgs += @("-Emulated")
181 | }
182 |
183 | $argString = "-NoProfile -NonInteractive -ExecutionPolicy Bypass `"& '$Script:InstallPath' $($cmdArgs -join ' ')`""
184 | Write-Verbose "Task executable: $Script:PowershellPath"
185 | Write-Verbose "Task arguments: $argString"
186 |
187 | $task = New-ScheduledTask `
188 | -Trigger (New-ScheduledTaskTrigger -AtStartup) `
189 | -Action (New-ScheduledTaskAction -Execute $Script:PowershellPath -Argument $argString) `
190 | -Principal (New-ScheduledTaskPrincipal -UserId "NT AUTHORITY\SYSTEM" -RunLevel Highest -LogonType ServiceAccount) `
191 | -Settings (New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -ExecutionTimeLimit (New-TimeSpan -Minutes 5))
192 | if ($PSCmdlet.ShouldProcess($Script:ScheduledTaskName, "Create scheduled task")) {
193 | $task | Register-ScheduledTask -TaskName $Script:ScheduledTaskName
194 | }
195 | }
196 |
--------------------------------------------------------------------------------