feat: Install VMWare driver during unattended setup (#2221)

This commit is contained in:
Kroese
2026-09-14 05:19:46 +02:00
committed by GitHub
parent e0026b432d
commit 792ff0d7ec
66 changed files with 2523 additions and 1082 deletions
+36 -58
View File
@@ -71,11 +71,6 @@ updateXML() {
return 1
fi
if ! updateLogonCommandXML "$asset"; then
error "Failed to update first-logon command in answer file!"
return 1
fi
if ! updateEditionXML "$asset"; then
error "Failed to update edition settings in answer file!"
return 1
@@ -526,49 +521,6 @@ updateAutologinXML() {
return 0
}
usesWscriptLogonLauncher() {
case "${DETECTED,,}" in
"winvista"* | "win7"* | "win2008r2"* ) return 0 ;;
esac
return 1
}
updateLogonCommandXML() {
local asset="$1"
local command="$XML_COMPONENT_SHELL_OOBE/u:FirstLogonCommands/u:SynchronousCommand/u:CommandLine"
local expected='cmd.exe /d /c call "%WINDIR%\Setup\Scripts\SetupComplete.cmd" logon'
local hidden
if usesWscriptLogonLauncher; then
hidden='wscript.exe //B //NoLogo C:\Windows\Setup\Scripts\RunHidden.vbs'
else
hidden="powershell.exe -NoLogo -NoProfile -NonInteractive -WindowStyle Hidden -Command \"\$cmd = 'call ' + [char]34 + \$env:WINDIR + '\Setup\Scripts\SetupComplete.cmd' + [char]34 + ' logon'; & \$env:ComSpec /d /c \$cmd; exit \$LASTEXITCODE\""
fi
local count value
count=$(getXMLNodeCount "$asset" "$command") || return 1
if [ "$count" != "1" ]; then
error "Failed to find a unique first-logon command in answer file: $asset"
return 1
fi
value=$(xmlstarlet sel -N "$XML_NS_UNATTEND_ARG" -T -t -v "string($command)" "$asset") || return 1
if [ "$value" != "$expected" ]; then
error "Unexpected first-logon command in answer file: $asset"
return 1
fi
xmlstarlet ed -L -N "$XML_NS_UNATTEND_ARG" -u "$command" -v "$hidden" "$asset" || return 1
return 0
}
updateProductKey() {
local script="$1"
@@ -1968,43 +1920,69 @@ prepareSetupScript() {
[ -n "$staged" ] || return 0
stageHiddenLogonLauncher "$stage" || return 1
stageUnattendLauncher "$stage" || return 1
updateSetupScript "$staged" "$asset" || return 1
finalizeSetupScript "$staged" || return 1
stageSetupCompleteWrapper "$stage" || return 1
return 0
}
stageHiddenLogonLauncher() {
stageSetupCompleteWrapper() {
local stage="$1"
local target="$stage/\$OEM\$/\$\$/Setup/Scripts/SetupComplete.cmd"
usesWscriptLogonLauncher || return 0
if ! cat > "$target" <<'EOF'
@echo off
call "%~dp0Unattend.cmd" setup
exit /b %errorlevel%
EOF
then
error "Failed to create SetupComplete wrapper!"
return 1
fi
local target="$stage/\$OEM\$/\$\$/Setup/Scripts/RunHidden.vbs"
if ! unix2dos -q "$target"; then
error "Failed to convert SetupComplete wrapper to DOS format!"
return 1
fi
return 0
}
stageUnattendLauncher() {
local stage="$1"
local target="$stage/\$OEM\$/\$\$/Setup/Scripts/Unattend.vbs"
if ! mkdir -p "$(dirname "$target")"; then
error "Failed to create hidden logon launcher directory!"
error "Failed to create unattended launcher directory!"
return 1
fi
if ! cat > "$target" <<'EOF'
Option Explicit
Dim shell, command, result
Dim shell, command, result, pass
If WScript.Arguments.Count <> 1 Then
WScript.Quit 1
End If
pass = WScript.Arguments(0)
Set shell = CreateObject("WScript.Shell")
command = shell.ExpandEnvironmentStrings("%ComSpec% /d /c call " & Chr(34) & "%WINDIR%\Setup\Scripts\SetupComplete.cmd" & Chr(34) & " logon")
command = shell.ExpandEnvironmentStrings("%ComSpec% /d /c call " & Chr(34) & "%WINDIR%\Setup\Scripts\Unattend.cmd" & Chr(34) & " " & pass)
result = shell.Run(command, 0, True)
WScript.Quit result
EOF
then
error "Failed to create hidden logon launcher!"
error "Failed to create unattended launcher!"
return 1
fi
if ! unix2dos -q "$target"; then
error "Failed to convert hidden logon launcher to DOS format!"
error "Failed to convert unattended launcher to DOS format!"
return 1
fi
@@ -2124,7 +2102,7 @@ stageSetupScript() {
source=$(findSetupScript "$asset") || return 1
[ -n "$source" ] || return 0
target="$stage/\$OEM\$/\$\$/Setup/Scripts/SetupComplete.cmd"
target="$stage/\$OEM\$/\$\$/Setup/Scripts/Unattend.cmd"
if ! mkdir -p "$(dirname "$target")"; then
error "Failed to create setup script directory!"
+63 -103
View File
@@ -138,7 +138,6 @@ Win9xInstall() {
local display="$win9x/vmdisp9x"
local audio95="$win9x/alcx95"
local audiowdm="$win9x/alcxwdm"
local vmware_display="$win9x/vmsvga"
extractDrivers "$drivers" || return 1
@@ -172,7 +171,7 @@ Win9xInstall() {
return 1
fi
if ! patchWin9xSetupFiles "$id" "$target" "$desc" "$patcher" "$qemouse" "$display" "$vmware_display"; then
if ! patchWin9xSetupFiles "$id" "$target" "$desc" "$patcher" "$qemouse" "$display"; then
rm -rf "$drivers" || :
return 1
fi
@@ -559,7 +558,6 @@ patchWin9xSetupFiles() {
local patcher="$4"
local qemouse="$5"
local display="$6"
local vmware_display="$7"
chmod 755 "$patcher" || {
error "Failed to make Patcher9x executable!"
@@ -584,12 +582,13 @@ patchWin9xSetupFiles() {
fi
stageWin9xDisplayDriver "$target" "$display" "$desc" || return 1
stageWin9xVMwareDriver "$target" "$vmware_display" "$desc" || return 1
if ! mv -f -- \
"$target/VMDISP9X/vmdisp9x.inf" \
"$target/VMDISP9X/qemumini.drv" \
"$target/VMDISP9X/qemumini.vxd" \
"$target/VMDISP9X/vmwsmini.drv" \
"$target/VMDISP9X/vmwsmini.vxd" \
"$target/VMDISP9X/vmhal9x.dll" \
"$target/VMDISP9X/vmhal486.dll" \
"$target/VMDISP9X/vmdisp9x.dll" \
@@ -1139,6 +1138,8 @@ stageWin9xDisplayDriver() {
vmdisp9x.inf \
qemumini.drv \
qemumini.vxd \
vmwsmini.drv \
vmwsmini.vxd \
vmhal9x.dll \
vmhal486.dll \
vmdisp9x.dll; do
@@ -1157,6 +1158,8 @@ stageWin9xDisplayDriver() {
"$source/vmdisp9x.inf" \
"$source/qemumini.drv" \
"$source/qemumini.vxd" \
"$source/vmwsmini.drv" \
"$source/vmwsmini.vxd" \
"$source/vmhal9x.dll" \
"$source/vmhal486.dll" \
"$source/vmdisp9x.dll" \
@@ -1166,6 +1169,50 @@ stageWin9xDisplayDriver() {
return 1
fi
# VMDisp9x identifies QEMU by the absence of SVGA_FIFO_CAP_FENCE. Our
# enhanced SVGA device supports fences, so force the driver's existing QEMU
# compatibility path instead. This keeps fence support while enabling its V86
# VGA-memory mapping workaround for KVM/WHPX shutdown. Match the surrounding
# machine code exactly and fail closed if an upstream driver build changes it.
if ! python3 - "$dest/vmwsmini.vxd" <<'PY'
from pathlib import Path
import sys
path = Path(sys.argv[1])
data = bytearray(path.read_bytes())
old = bytes.fromhex(
"6A 01 "
"E8 CE 0B 00 00 "
"83 C4 04 "
"85 C0 "
"75 48 "
"68 20 00 00 00 "
"68 CE 01 00 00"
)
new = old[:12] + b"\x90\x90" + old[14:]
old_count = bytes(data).count(old)
new_count = bytes(data).count(new)
if old_count != 1 or new_count != 0:
raise SystemExit(
"VMDisp9x QEMU compatibility signature mismatch: "
f"original={old_count}, patched={new_count}"
)
offset = bytes(data).index(old)
data[offset + 12:offset + 14] = b"\x90\x90"
path.write_bytes(data)
verify = path.read_bytes()
if verify.count(old) != 0 or verify.count(new) != 1:
raise SystemExit("VMDisp9x QEMU compatibility patch verification failed")
PY
then
error "Failed to enable the VMDisp9x QEMU compatibility path!"
return 1
fi
# VMDisp9x's DDC flag makes Win9x enumerate a Plug and Play monitor after
# the display driver starts. The unattended setup already selects the monitor
# and display mode, and VMDisp9x carries a fixed mode list, so disable DDC in
@@ -1181,100 +1228,6 @@ stageWin9xDisplayDriver() {
return 1
fi
# Keep VMDisp9x available for QEMU STD VGA, but let the official VMware
# driver own VMware SVGA-II. Comment only the matching model in our staged
# INF so the original driver archive remains untouched.
if ! python3 - "$dest/vmdisp9x.inf" <<'PY'
from pathlib import Path
import sys
path = Path(sys.argv[1])
lines = path.read_bytes().splitlines(keepends=True)
needle = br'PCI\VEN_15AD&DEV_0405&SUBSYS_040515AD'
matches = []
for index, line in enumerate(lines):
stripped = line.lstrip(b' \t')
if stripped.startswith(b';'):
continue
if needle in stripped.upper():
matches.append(index)
if len(matches) != 1:
raise SystemExit(f'expected exactly one active VMware SVGA-II model, found {len(matches)}')
index = matches[0]
indent = len(lines[index]) - len(lines[index].lstrip(b' \t'))
lines[index] = lines[index][:indent] + b'; ' + lines[index][indent:]
path.write_bytes(b''.join(lines))
PY
then
error "Failed to reserve VMware SVGA-II for the official VMware driver!"
return 1
fi
if grep -iF 'PCI\VEN_15AD&DEV_0405&SUBSYS_040515AD' "$dest/vmdisp9x.inf" |
grep -Ev '^[[:space:]]*;' >/dev/null; then
error "Failed to remove the active VMware SVGA-II VMDisp9x model!"
return 1
fi
if ! grep -iF 'PCI\VEN_15AD&DEV_0405&SUBSYS_040515AD' "$dest/vmdisp9x.inf" |
grep -Eq '^[[:space:]]*;'; then
error "Failed to verify the VMware SVGA-II VMDisp9x model change!"
return 1
fi
return 0
}
stageWin9xVMwareDriver() {
local target="$1"
local source="$2"
local desc="$3"
local file
for file in \
vmx_svga.inf \
VMX_SVGA.DRV \
VMX_SVGA.vxd; do
if [ ! -s "$source/$file" ]; then
error "Failed to locate required VMware SVGA driver file: $file"
return 1
fi
done
if ! cp -f -- \
"$source/vmx_svga.inf" \
"$source/VMX_SVGA.DRV" \
"$source/VMX_SVGA.vxd" \
"$target/"; then
error "Failed to add the VMware SVGA driver to $desc setup files!"
return 1
fi
for file in \
vmx_svga.inf \
VMX_SVGA.DRV \
VMX_SVGA.vxd; do
if ! cmp -s -- "$source/$file" "$target/$file"; then
error "Failed to verify the staged VMware SVGA driver file: $file"
return 1
fi
done
if ! grep -Fqi 'PCI\VEN_15AD&DEV_0405' "$target/vmx_svga.inf"; then
error "Failed to verify the VMware SVGA-II hardware ID!"
return 1
fi
return 0
}
@@ -3210,15 +3163,22 @@ createWin9xSystemImage() {
fi
local boot_gui=0
[[ "${id,,}" == "win9x"* ]] && boot_gui=1
[[ "${id,,}" == "win9x" ]] && boot_gui=1
local auto_scan=2
[[ "${id,,}" == "win95" ]] && auto_scan=0
{
printf '%s\n' \
'[Options]' \
"[Options]" \
"BootGUI=$boot_gui" \
'BootDelay=0' \
'AutoScan=2' \
'Logo=0' \
"BootMenu=0" \
"BootMenuDefault=1" \
"BootWarn=0" \
"BootSafe=0" \
"BootDelay=0" \
"AutoScan=$auto_scan" \
"Logo=0" \
''
} | unix2dos > "$msdos" || return 1
+1106
View File
File diff suppressed because it is too large Load Diff
+9 -8
View File
@@ -1423,8 +1423,7 @@ addDrivers() {
fi
if ! bsdtar -xf /var/drivers.txz -C "$drivers" \
--exclude='win9x' --exclude='sata' --exclude='qbochs' \
--exclude='vmsvga'; then
--exclude='win9x' --exclude='sata' --exclude='qbochs'; then
error "Failed to extract drivers from archive!" && return 1
fi
@@ -1439,11 +1438,10 @@ addDrivers() {
mkdir -p "$dst" || return 1
cp -Lr "$dest/." "$dst" || return 1
# Install the VirtIO display driver explicitly from SetupComplete.cmd so it
# Install display drivers explicitly from Unattend.cmd so it
# cannot disrupt Windows Setup by loading through the WinPE driver path.
if ! isLegacy "$version"; then
rm -rf "$dest/viogpudo" || return 1
fi
rm -rf "$dest/vmsvga" || return 1
rm -rf "$dest/viogpudo" || return 1
local winpe="$stage/$target"
rm -rf "$winpe" || return 1
@@ -1470,6 +1468,7 @@ selectDrivers() {
viorng
viostor
viomem
vmsvga
NetKVM
Balloon
vioscsi
@@ -1723,8 +1722,10 @@ setDiskMinimum() {
local id="$1"
local required
required=$(getRequiredDisk "$id") || return
DISK_MINIMUM="$required"
if [ -z "${DISK_MINIMUM:-}" ]; then
required=$(getRequiredDisk "$id") || return
DISK_MINIMUM="$required"
fi
return 0
}
+13 -12
View File
@@ -26,18 +26,6 @@ setMachine() {
esac
case "${id,,}" in
"winnt4" )
writeState "vga" "cirrus" || return 1 ;;
"win9"* | "win2k"* | "winxp"* | "win2003"* )
writeState "vga" "vmware" || return 1 ;;
*) writeState "vga" "std" || return 1 ;;
esac
case "${id,,}" in
"win9"* | "winnt4" )
@@ -76,6 +64,19 @@ setMachine() {
esac
fi
case "${id,,}" in
"winnt4" )
writeState "vga" "cirrus" || return 1 ;;
"win9"* | "win2k"* | "winxp"* | \
"win2003"* | "win2008"* | "win2012"*| \
"winvista"* | "win7"* | "win8"* | "reactos" )
writeState "vga" "vmvga" || return 1 ;;
esac
restoreMachine || return 1
restoreBootMode || return 1
+2 -8
View File
@@ -301,19 +301,15 @@ addVMSVGADriver() {
local drivers="$4"
local vmsvga_arch="$arch"
[[ "${vmsvga_arch,,}" == "amd64" ]] && vmsvga_arch="x64"
local source="$drivers/vmsvga/$driver/$vmsvga_arch"
local destination="$dir/\$OEM\$/\$1/Drivers/VMSVGA"
local destination="$dir/\$OEM\$/\$1/Drivers/vmsvga"
if [ ! -d "$source" ]; then
error "Failed to locate required VMware SVGA display driver directory: $source"
return 1
fi
local files="vmx_svgaver.dll vmx_svga.cat vmx_mode.dll vmx_svga.sys vmwogl32.dll vmx_fb.dll vmx_svga.inf"
[[ "$vmsvga_arch" == "x64" ]] && files+=" vmwogl64.dll"
local files="vmx_svgaver.dll vmx_svga.cat vmx_mode.dll vmx_svga.sys vmx_fb.dll vmx_svga.inf"
local file
for file in $files; do
@@ -339,8 +335,6 @@ addDisplayDriver() {
local drivers="$4"
local qbochs_arch="$arch"
[[ "${qbochs_arch,,}" == "amd64" ]] && qbochs_arch="x64"
local source="$drivers/qbochs/$driver/$qbochs_arch"
local destination="$dir/\$OEM\$/\$1/Drivers/QBochs"