Skip to content

Commit aa4211b

Browse files
committed
feat(quickemu): add CPU flag management with deduplication
- Add CPU_FLAG_MAP and helper functions reset_cpu_flags and add_cpu_flag - Validate flag format, deduplicate entries and detect +/- and value conflicts - Replace direct CPU string concatenation in configure_cpu with add_cpu_flag calls - Centralise CPU flag logic to improve macOS and Windows CPU feature selection IMPACT: prevents duplicate or conflicting CPU flags being passed to QEMU and simplifies future CPU feature handling. Signed-off-by: Martin Wimpress <martin@wimpress.org>
1 parent 6e0b498 commit aa4211b

File tree

1 file changed

+94
-8
lines changed

1 file changed

+94
-8
lines changed

quickemu

Lines changed: 94 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,68 @@ function efi_vars() {
411411
fi
412412
}
413413

414+
# Reset CPU flags tracking (call when initialising new CPU string)
415+
function reset_cpu_flags() {
416+
CPU_FLAG_MAP=()
417+
}
418+
419+
# Add a CPU flag with deduplication and conflict detection
420+
# Usage: add_cpu_flag "+vmx" or add_cpu_flag ",+vmx"
421+
function add_cpu_flag() {
422+
local flag="${1#,}" # Strip leading comma if present
423+
424+
# Skip empty flags
425+
[[ -z "${flag}" ]] && return 0
426+
427+
# Validate flag format: must be [+-]?name or name=value
428+
if [[ ! "${flag}" =~ ^[+-]?[a-zA-Z][a-zA-Z0-9._-]*(=.+)?$ ]]; then
429+
echo "WARNING: Invalid CPU flag format: '${flag}' - skipping"
430+
return 1
431+
fi
432+
433+
# Extract base flag name (without +/- prefix and =value suffix)
434+
local prefix=""
435+
local base="${flag}"
436+
if [[ "${flag}" == [+-]* ]]; then
437+
prefix="${flag:0:1}"
438+
base="${flag:1}"
439+
fi
440+
base="${base%%=*}"
441+
442+
# Check for exact duplicate
443+
if [[ -n "${CPU_FLAG_MAP[${flag}]:-}" ]]; then
444+
return 0 # Silently skip duplicates
445+
fi
446+
447+
# Check for conflicts (opposite prefix)
448+
local opposite=""
449+
if [[ "${prefix}" == "+" ]]; then
450+
opposite="-${base}"
451+
elif [[ "${prefix}" == "-" ]]; then
452+
opposite="+${base}"
453+
fi
454+
455+
if [[ -n "${opposite}" ]] && [[ -n "${CPU_FLAG_MAP[${opposite}]:-}" ]]; then
456+
echo "WARNING: Conflicting CPU flag '${flag}' ignored (${opposite} already set)"
457+
return 1
458+
fi
459+
460+
# Check for value conflicts (e.g., flag=on vs flag=off)
461+
if [[ "${flag}" == *=* ]]; then
462+
for existing in "${!CPU_FLAG_MAP[@]}"; do
463+
if [[ "${existing%%=*}" == "${base}" ]] && [[ "${existing}" == *=* ]]; then
464+
echo "WARNING: Conflicting CPU flag '${flag}' ignored (${existing} already set)"
465+
return 1
466+
fi
467+
done
468+
fi
469+
470+
# Add to tracking map and append to CPU string
471+
CPU_FLAG_MAP["${flag}"]=1
472+
CPU+=",${flag}"
473+
return 0
474+
}
475+
414476
function configure_cpu() {
415477
HOST_CPU_CORES=$(get_nproc)
416478
HOST_CPU_MODEL=$(get_cpu_info '^Model name:')
@@ -499,6 +561,7 @@ function configure_cpu() {
499561
fi
500562

501563
CPU="-cpu ${CPU_MODEL}"
564+
reset_cpu_flags
502565

503566
# Make any OS specific adjustments
504567
if [ "${guest_os}" == "freedos" ] || [ "${guest_os}" == "windows" ] || [ "${guest_os}" == "windows-server" ]; then
@@ -522,6 +585,7 @@ function configure_cpu() {
522585
batocera|freedos|haiku|solaris) MACHINE_TYPE="pc";;
523586
kolibrios|reactos)
524587
CPU="-cpu qemu32"
588+
reset_cpu_flags
525589
MACHINE_TYPE="pc";;
526590
macos)
527591
# If the host has an Intel CPU, passes the host CPU model features, model, stepping, exactly to the guest.
@@ -530,9 +594,11 @@ function configure_cpu() {
530594
if [ "${HOST_CPU_VENDOR}" == "GenuineIntel" ] && [ -z "${HYPERVISOR}" ]; then
531595
CPU_MODEL="host"
532596
CPU="-cpu ${CPU_MODEL},-pdpe1gb,+hypervisor"
597+
reset_cpu_flags
533598
else
534599
CPU_MODEL="Haswell-v2"
535600
CPU="-cpu ${CPU_MODEL},vendor=GenuineIntel,-pdpe1gb,+avx,+sse,+sse2,+ssse3,vmware-cpuid-freq=on"
601+
reset_cpu_flags
536602
fi
537603
# A CPU with fma is required for Metal support
538604
# A CPU with invtsc is required for macOS to boot
@@ -544,7 +610,8 @@ function configure_cpu() {
544610
# A CPU with AVX2 support is required for >= macOS Ventura
545611
if check_cpu_flag sse4_2 && check_cpu_flag avx2; then
546612
if [ "${HOST_CPU_VENDOR}" != "GenuineIntel" ] && [ -z "${HYPERVISOR}" ]; then
547-
CPU+=",+avx2,+sse4.2"
613+
add_cpu_flag "+avx2"
614+
add_cpu_flag "+sse4.2"
548615
fi
549616
else
550617
echo "ERROR! macOS ${macos_release} requires a CPU with SSE 4.2 and AVX2 support."
@@ -555,7 +622,7 @@ function configure_cpu() {
555622
# A CPU with SSE4.2 support is required for >= macOS Catalina
556623
if check_cpu_flag sse4_2; then
557624
if [ "${HOST_CPU_VENDOR}" != "GenuineIntel" ] && [ -z "${HYPERVISOR}" ]; then
558-
CPU+=",+sse4.2"
625+
add_cpu_flag "+sse4.2"
559626
fi
560627
else
561628
echo "ERROR! macOS ${macos_release} requires a CPU with SSE 4.2 support."
@@ -565,7 +632,7 @@ function configure_cpu() {
565632
# A CPU with SSE4.1 support is required for >= macOS Sierra
566633
if check_cpu_flag sse4_1; then
567634
if [ "${HOST_CPU_VENDOR}" != "GenuineIntel" ] && [ -z "${HYPERVISOR}" ]; then
568-
CPU+=",+sse4.1"
635+
add_cpu_flag "+sse4.1"
569636
fi
570637
else
571638
echo "ERROR! macOS ${macos_release} requires a CPU with SSE 4.1 support."
@@ -579,12 +646,15 @@ function configure_cpu() {
579646
mca mce mmx movbe mpx msr mtrr nx pae pat pcid pge pse popcnt pse36 \
580647
rdrand rdtscp sep smep syscall tsc tsc_adjust vaes vbmi2 vmx vpclmulqdq \
581648
x2apic xgetbv1 xsave xsaveopt; do
582-
CPU+=$(configure_cpu_flag "${FLAG}")
649+
local cpu_flag
650+
cpu_flag=$(configure_cpu_flag "${FLAG}")
651+
[[ -n "${cpu_flag}" ]] && add_cpu_flag "${cpu_flag#,}"
583652
done
584653
# AMD CPUs with constant_tsc need explicit TSC flags for macOS stability
585654
# constant_tsc is AMD's equivalent of Intel's invtsc
586655
if [ "${HOST_CPU_VENDOR}" == "AuthenticAMD" ] && check_cpu_flag invtsc; then
587-
CPU+=",+tsc,+tsc-deadline"
656+
add_cpu_flag "+tsc"
657+
add_cpu_flag "+tsc-deadline"
588658
fi
589659
fi
590660
fi
@@ -602,12 +672,25 @@ function configure_cpu() {
602672
windows|windows-server)
603673
# Base CPU flags that work with all accelerators (KVM, HVF, TCG)
604674
CPU="-cpu ${CPU_MODEL},+hypervisor,+invtsc,l3-cache=on"
675+
reset_cpu_flags
605676
# KVM-specific flags: migratable and Hyper-V enlightenments
606677
if [ "${QEMU_ACCEL}" == "kvm" ]; then
607678
if [ "${QEMU_VER_SHORT}" -gt 60 ]; then
608-
CPU+=",migratable=no,hv_passthrough"
679+
add_cpu_flag "migratable=no"
680+
add_cpu_flag "hv_passthrough"
609681
else
610-
CPU+=",migratable=no,hv_frequencies${CPU_KVM_UNHALT},hv_reenlightenment,hv_relaxed,hv_spinlocks=8191,hv_stimer,hv_synic,hv_time,hv_vapic,hv_vendor_id=1234567890ab,hv_vpindex"
682+
add_cpu_flag "migratable=no"
683+
add_cpu_flag "hv_frequencies"
684+
[[ -n "${CPU_KVM_UNHALT}" ]] && add_cpu_flag "${CPU_KVM_UNHALT#,}"
685+
add_cpu_flag "hv_reenlightenment"
686+
add_cpu_flag "hv_relaxed"
687+
add_cpu_flag "hv_spinlocks=8191"
688+
add_cpu_flag "hv_stimer"
689+
add_cpu_flag "hv_synic"
690+
add_cpu_flag "hv_time"
691+
add_cpu_flag "hv_vapic"
692+
add_cpu_flag "hv_vendor_id=1234567890ab"
693+
add_cpu_flag "hv_vpindex"
611694
fi
612695
fi
613696
# Disable S3 support in the VM to ensure Windows can boot with SecureBoot enabled
@@ -624,7 +707,7 @@ function configure_cpu() {
624707
esac
625708

626709
if [ "${HOST_CPU_VENDOR}" == "AuthenticAMD" ] && [ "${guest_os}" != "macos" ]; then
627-
CPU+=",topoext"
710+
add_cpu_flag "topoext"
628711
fi
629712

630713
if [ -z "${cpu_cores}" ]; then
@@ -2308,6 +2391,9 @@ VMDIR=""
23082391
VMNAME=""
23092392
VMPATH=""
23102393

2394+
# CPU flag tracking map for deduplication and conflict detection
2395+
declare -A CPU_FLAG_MAP
2396+
23112397
# shellcheck disable=SC2155
23122398
readonly LAUNCHER=$(basename "${0}")
23132399
readonly DISK_MIN_SIZE=$((197632 * 8))

0 commit comments

Comments
 (0)