using QemuVmManager.Models; using System.Diagnostics; using System.Runtime.InteropServices; namespace QemuVmManager.Core; public class QemuCommandBuilder { private readonly VmConfiguration _config; private readonly List _arguments = new(); private readonly VirtualizationType _virtualizationType; public QemuCommandBuilder(VmConfiguration config, VirtualizationType virtualizationType = VirtualizationType.TCG) { _config = config; _virtualizationType = virtualizationType; } public string BuildCommand() { _arguments.Clear(); // Basic QEMU command _arguments.Add("qemu-system-x86_64"); // Machine and CPU configuration AddMachineConfiguration(); AddCpuConfiguration(); AddMemoryConfiguration(); // Storage configuration AddStorageConfiguration(); // Network configuration AddNetworkConfiguration(); // Display configuration AddDisplayConfiguration(); // Boot configuration AddBootConfiguration(); // Advanced features AddAdvancedConfiguration(); // Extra arguments _arguments.AddRange(_config.Advanced.ExtraArgs); // Handle CPU model based on virtualization type if (_virtualizationType == VirtualizationType.TCG || _virtualizationType == VirtualizationType.HyperV) { // Replace 'host' CPU model with 'qemu64' for TCG compatibility for (int i = 0; i < _arguments.Count; i++) { if (_arguments[i] == "-cpu" && i + 1 < _arguments.Count && _arguments[i + 1] == "host") { _arguments[i + 1] = "qemu64"; } } } // Remove -enable-kvm if not using KVM if (_virtualizationType != VirtualizationType.KVM) { _arguments.RemoveAll(arg => arg == "-enable-kvm"); } // Add WHPX-specific configurations if (_virtualizationType == VirtualizationType.HyperV) { AddWHPXSpecificConfiguration(); } return string.Join(" ", _arguments); } private void AddMachineConfiguration() { _arguments.Add("-machine"); switch (_virtualizationType) { case VirtualizationType.KVM: _arguments.Add("type=q35,accel=kvm:tcg"); break; case VirtualizationType.HyperV: // Use WHPX hardware acceleration _arguments.Add("type=pc-i440fx-10.1,accel=whpx:tcg,kernel-irqchip=off"); break; case VirtualizationType.HAXM: _arguments.Add("type=q35,accel=hax:tcg"); break; case VirtualizationType.HVF: _arguments.Add("type=q35,accel=hvf:tcg"); break; case VirtualizationType.TCG: default: _arguments.Add("type=q35,accel=tcg"); break; } } private void AddCpuConfiguration() { var cpu = _config.Cpu; _arguments.Add("-cpu"); _arguments.Add(cpu.Model); // For WHPX, use simpler CPU configuration to avoid exit code 4 if (_virtualizationType == VirtualizationType.HyperV) { _arguments.Add("-smp"); _arguments.Add($"{Math.Min(cpu.Cores, 2)},cores={Math.Min(cpu.Cores, 2)},sockets=1,threads=1"); } else { _arguments.Add("-smp"); _arguments.Add($"{cpu.Cores},cores={cpu.Cores},sockets={cpu.Sockets},threads={cpu.Threads}"); } } private void AddMemoryConfiguration() { var memory = _config.Memory; // For WHPX, use smaller memory allocation to avoid exit code 4 if (_virtualizationType == VirtualizationType.HyperV) { var whpxMemorySize = Math.Min(memory.Size, 4096); // Limit to 4GB for WHPX _arguments.Add("-m"); _arguments.Add($"{whpxMemorySize}{memory.Unit}"); } else { _arguments.Add("-m"); _arguments.Add($"{memory.Size}{memory.Unit}"); } } private void AddStorageConfiguration() { var storage = _config.Storage; // Add disks for (int i = 0; i < storage.Disks.Count; i++) { var disk = storage.Disks[i]; var driveLetter = (char)('a' + i); _arguments.Add("-drive"); var driveArgs = $"file={disk.Path.Replace('\\', '/')},format={disk.Format},cache={disk.Cache},id=drive{i},if=none"; _arguments.Add(driveArgs); // For WHPX, only use the first disk on IDE to avoid conflicts if (_virtualizationType == VirtualizationType.HyperV && i > 0) { Console.WriteLine($"Warning: Additional disk {i} disabled for WHPX compatibility to avoid IDE conflicts. Disk: {disk.Path}"); continue; } // Add device _arguments.Add("-device"); if (disk.Interface == "virtio" && _virtualizationType != VirtualizationType.HyperV) { // Use virtio for better performance, but avoid with WHPX due to MSI issues _arguments.Add($"virtio-blk-pci,drive=drive{i}"); } else { // Use IDE for WHPX compatibility _arguments.Add($"ide-hd,drive=drive{i}"); } } // Add CD-ROM if specified if (!string.IsNullOrEmpty(storage.Cdrom)) { if (_virtualizationType == VirtualizationType.HyperV) { // For WHPX, use the second IDE controller for CD-ROM _arguments.Add("-drive"); _arguments.Add($"file={storage.Cdrom.Replace('\\', '/')},media=cdrom,if=ide,index=1"); } else { // Use standard CD-ROM for other virtualization types _arguments.Add("-cdrom"); _arguments.Add(storage.Cdrom.Replace('\\', '/')); } } } private void AddNetworkConfiguration() { var network = _config.Network; for (int i = 0; i < network.Interfaces.Count; i++) { var nic = network.Interfaces[i]; // Use user network as fallback since bridge might not be available _arguments.Add("-netdev"); var netdevArgs = $"user,id=net{i}"; _arguments.Add(netdevArgs); _arguments.Add("-device"); var deviceArgs = ""; // Use different network models based on virtualization type if (nic.Model == "virtio-net-pci" && _virtualizationType == VirtualizationType.HyperV) { // Use e1000 for WHPX compatibility to avoid MSI issues deviceArgs = $"e1000,netdev=net{i}"; } else { deviceArgs = $"{nic.Model},netdev=net{i}"; } if (!string.IsNullOrEmpty(nic.Mac)) { deviceArgs += $",mac={nic.Mac}"; } _arguments.Add(deviceArgs); } } private void AddDisplayConfiguration() { var display = _config.Display; _arguments.Add("-display"); // Use appropriate display backend based on OS string displayType = display.Type; if (OperatingSystem.IsMacOS()) { // On macOS, GTK is not available, use cocoa instead if (displayType == "gtk") { displayType = "cocoa"; } } _arguments.Add(displayType); _arguments.Add("-vga"); _arguments.Add(display.Vga); if (display.EnableSpice) { _arguments.Add("-spice"); _arguments.Add($"port={display.SpicePort},addr=127.0.0.1,disable-ticketing=on"); } } private void AddBootConfiguration() { var boot = _config.Boot; if (boot.Order.Count > 0) { _arguments.Add("-boot"); _arguments.Add($"order={string.Join("", boot.Order)}"); } if (!string.IsNullOrEmpty(boot.Kernel)) { _arguments.Add("-kernel"); _arguments.Add(boot.Kernel); } if (!string.IsNullOrEmpty(boot.Initrd)) { _arguments.Add("-initrd"); _arguments.Add(boot.Initrd); } if (!string.IsNullOrEmpty(boot.Cmdline)) { _arguments.Add("-append"); _arguments.Add(boot.Cmdline); } } private void AddAdvancedConfiguration() { var advanced = _config.Advanced; if (advanced.EnableAudio) { _arguments.Add("-device"); _arguments.Add("intel-hda"); _arguments.Add("-device"); _arguments.Add("hda-duplex"); } if (advanced.EnableUsb) { _arguments.Add("-usb"); _arguments.Add("-device"); _arguments.Add("usb-tablet"); } // Disable virtio devices for WHPX to avoid MSI issues if (advanced.EnableBalloon && _virtualizationType != VirtualizationType.HyperV) { _arguments.Add("-device"); _arguments.Add("virtio-balloon-pci"); } if (advanced.EnableVirtioRng && _virtualizationType != VirtualizationType.HyperV) { _arguments.Add("-device"); _arguments.Add("virtio-rng-pci"); } if (advanced.EnableVirtioFs && _virtualizationType != VirtualizationType.HyperV) { _arguments.Add("-device"); _arguments.Add("virtio-fs-pci"); } // Add shared folders (disabled for compatibility) // Note: 9p filesystem support is not available in all QEMU builds // For now, shared folders are disabled to ensure compatibility if (advanced.SharedFolders.Any()) { // Log that shared folders are disabled Console.WriteLine($"Warning: Shared folders are disabled for compatibility. {advanced.SharedFolders.Count} folder(s) configured but not used."); } } private void AddWHPXSpecificConfiguration() { // Add WHPX-specific configurations to avoid MSI issues // Disable MSI for better WHPX compatibility _arguments.Add("-global"); _arguments.Add("pcie-root-port.msi=off"); // Use legacy interrupt mode for better compatibility _arguments.Add("-global"); _arguments.Add("pcie-root-port.msix=off"); // Add additional WHPX optimizations _arguments.Add("-global"); _arguments.Add("pcie-root-port.ari=off"); // Add WHPX-specific optimizations _arguments.Add("-rtc"); _arguments.Add("base=localtime"); // Memory allocation optimizations for WHPX // Note: -mem-path is not needed for WHPX and can cause issues // Memory preallocation is not available in this QEMU build // Add additional WHPX optimizations to avoid exit code 4 _arguments.Add("-no-reboot"); _arguments.Add("-no-shutdown"); // Use simpler interrupt handling (removed KVM-specific option) // Disable some features that might cause issues with WHPX // _arguments.Add("-no-acpi"); // Commented out as it might cause issues } }