diff --git a/BM/instruction-check/feature_list.py b/BM/instruction-check/feature_list.py index ee784bd9..9accaf2c 100755 --- a/BM/instruction-check/feature_list.py +++ b/BM/instruction-check/feature_list.py @@ -34,6 +34,10 @@ "cpuid": ['7', '0', '0', '0', 'b', '15'], "platforms": {"SPR", "EMR", "GNR", "SRF", "CWF"} }, + "AVX2": { + "cpuid": ['7', '0', '0', '0', 'b', '5'], + "platforms": {"SPR", "EMR", "GNR", "SRF", "CWF"} + }, "AVX512_F": { "cpuid": ['7', '0', '0', '0', 'b', '16'], "platforms": {"SPR", "EMR", "GNR"} @@ -142,6 +146,10 @@ "cpuid": ['7', '0', '0', '0', 'd', '8'], "platforms": {} }, + "AVX512_BF16": { + "cpuid": ['7', '0', '1', '0', 'a', '5'], + "platforms": {"SPR", "EMR", "GNR", "DMR"} + }, "SERIALIZE": { "cpuid": ['7', '0', '0', '0', 'd', '14'], "platforms": {"SPR", "EMR", "GNR", "SRF", "CWF"} @@ -294,10 +302,6 @@ "cpuid": ['1E', '0', '1', '0', 'a', '4'], "platforms": {"DMR"} }, - "AMX_TF32": { - "cpuid": ['1E', '0', '1', '0', 'a', '6'], - "platforms": {"DMR"} - }, "AMX_AVX512": { "cpuid": ['1E', '0', '1', '0', 'a', '7'], "platforms": {"DMR"} @@ -305,6 +309,26 @@ "AMX_MOVRS": { "cpuid": ['1E', '0', '1', '0', 'a', '8'], "platforms": {"DMR"} + }, + "ADX": { + "cpuid": ['7', '0', '0', '0', 'b', '19'], + "platforms": {"SPR", "EMR", "GNR", "SRF", "CWF", "DMR"} + }, + "VMX": { + "cpuid": ['1', '0', '0', '0', 'c', '5'], + "platforms": {"SPR", "EMR", "GNR", "SRF", "CWF", "DMR"} + }, + "IBRS": { + "cpuid": ['7', '0', '0', '0', 'd', '26'], + "platforms": {"SPR", "EMR", "GNR", "SRF", "CWF", "DMR"} + }, + "ARCH_CAPABILITIES": { + "cpuid": ['7', '0', '0', '0', 'd', '29'], + "platforms": {"SPR", "EMR", "GNR", "SRF", "CWF", "DMR"} + }, + "SSBD": { + "cpuid": ['7', '0', '0', '0', 'd', '31'], + "platforms": {"SPR", "EMR", "GNR", "SRF", "CWF", "DMR"} } # Add more feature_info here } diff --git a/BM/tdx-compliance/tdx-compliance.h b/BM/tdx-compliance/tdx-compliance.h index f91a93fd..440a07bc 100644 --- a/BM/tdx-compliance/tdx-compliance.h +++ b/BM/tdx-compliance/tdx-compliance.h @@ -11,6 +11,11 @@ #define X86_CR4_CET_BIT 23 /* enable Control-flow Enforcement Technology */ #define X86_CR4_CET _BITUL(X86_CR4_CET_BIT) #define MSR_IA32_VMX_PROCBASED_CTLS3 0x00000492 +/* Compatibility: kzalloc_obj may not be available in older kernels */ +#ifndef kzalloc_obj +#define kzalloc_obj(type, flags) ((type *)kzalloc(sizeof(type), flags)) +#endif + #define MSR_IA32_U_CET 0x000006a0 /* user mode cet */ #define MSR_IA32_S_CET 0x000006a2 /* kernel mode cet */ #define MSR_IA32_PL0_SSP 0x000006a4 /* ring-0 shadow stack pointer */ @@ -98,9 +103,9 @@ struct tdx_module_args { }; /* Used to communicate with the TDX module */ -extern u64 __tdcall(u64 fn, struct tdx_module_args *args); -extern u64 __tdcall_ret(u64 fn, struct tdx_module_args *args); -extern u64 __tdcall_saved_ret(u64 fn, struct tdx_module_args *args); +u64 __tdcall(u64 fn, struct tdx_module_args *args); +u64 __tdcall_ret(u64 fn, struct tdx_module_args *args); +u64 __tdcall_saved_ret(u64 fn, struct tdx_module_args *args); u64 tdcall(u64 fn, struct tdx_module_args *args); /* Used to request services from the VMM */ diff --git a/BM/topology/tests-cache_topo b/BM/topology/tests-cache_topo new file mode 100644 index 00000000..d9c74246 --- /dev/null +++ b/BM/topology/tests-cache_topo @@ -0,0 +1,10 @@ +# This file collects the CPU Topology case that verifies DMR cache topology +# (leaf 0x04H L1/L2/L3 sharing) inside a guest booted with a single socket +# DMR topology: +# -smp cpus=192,sockets=1,dies=4,modules=24,cores=2,threads=1 \ +# -machine smp-cache.0.cache=l2,smp-cache.0.topology=module +# @hw_dep: +# @other_dep: +# @other_warn: which cpuid 2>/dev/null @ cpuid tool is required + +cpu_topology.sh -t verify_qemu_dmr_cache_topo_cpuid diff --git a/BM/topology/tests-cores_per_socket b/BM/topology/tests-cores_per_socket new file mode 100644 index 00000000..aa81a91e --- /dev/null +++ b/BM/topology/tests-cores_per_socket @@ -0,0 +1,8 @@ +# This file collects the CPU Topology case that verifies the number of cores +# per socket is consistent between sysfs and lscpu inside a guest booted with +# a two sockets topology: +# -smp cpus=192,sockets=2,dies=2,modules=24,cores=2,threads=1 +# @hw_dep: +# @other_dep: + +cpu_topology.sh -t verify_cores_per_socket diff --git a/BM/topology/tests-die_cluster_1 b/BM/topology/tests-die_cluster_1 new file mode 100644 index 00000000..dd6a91a0 --- /dev/null +++ b/BM/topology/tests-die_cluster_1 @@ -0,0 +1,10 @@ +# This file collects the CPU Topology case that verifies DMR CPUID 0x1f +# level types (thread/core/module/die) inside a guest booted with a single +# socket DMR topology: +# -smp cpus=192,sockets=1,dies=4,modules=24,cores=2,threads=1 \ +# -machine smp-cache.0.cache=l2,smp-cache.0.topology=module +# @hw_dep: +# @other_dep: +# @other_warn: which cpuid 2>/dev/null @ cpuid tool is required + +cpu_topology.sh -t verify_qemu_dmr_one_socket_cpuid diff --git a/BM/topology/tests-die_cluster_2 b/BM/topology/tests-die_cluster_2 new file mode 100644 index 00000000..ab75f635 --- /dev/null +++ b/BM/topology/tests-die_cluster_2 @@ -0,0 +1,10 @@ +# This file collects the CPU Topology case that verifies DMR CPUID 0x1f +# level types (thread/core/module/die) inside a guest booted with a two +# sockets DMR topology: +# -smp cpus=192,sockets=2,dies=2,modules=24,cores=2,threads=1 \ +# -machine smp-cache.0.cache=l2,smp-cache.0.topology=module +# @hw_dep: +# @other_dep: +# @other_warn: which cpuid 2>/dev/null @ cpuid tool is required + +cpu_topology.sh -t verify_qemu_dmr_two_sockets_cpuid diff --git a/BM/topology/tests-sockets_number b/BM/topology/tests-sockets_number new file mode 100644 index 00000000..e2d794f3 --- /dev/null +++ b/BM/topology/tests-sockets_number @@ -0,0 +1,9 @@ +# This file collects the CPU Topology case that verifies the number of sockets +# is consistent between lscpu and sysfs (and matches the NUMA node count) +# inside a guest booted with a two sockets, two NUMA nodes topology: +# -smp cpus=192,sockets=2,dies=2,modules=24,cores=2,threads=1 +# with one NUMA node per socket +# @hw_dep: +# @other_dep: + +cpu_topology.sh -t verify_socket_num diff --git a/KVM/docs/tdx_temp.cfg b/KVM/docs/tdx_temp.cfg index 965310b6..95da3062 100644 --- a/KVM/docs/tdx_temp.cfg +++ b/KVM/docs/tdx_temp.cfg @@ -34,6 +34,8 @@ image_name = /root/avocado/data/avocado-vt/images/rhel-guest-image-9.4-20240419. image_format = qcow2 #image_name = /root/avocado/data/avocado-vt/images/tdx_guest #image_format = raw +# image_name = /root/avocado/data/avocado-vt/images/ia32e_win11 +# image_format = raw #For RHEL9.4 guest shell_prompt = "^\[.*\][\#\$]\s*$" @@ -74,9 +76,13 @@ only x86_64 only RHEL.9 #For Ubuntu guest #only Linux.Ubuntu.14.04.3-server.x86_64 +#For Windows guest +#only Windows +#only Win10 only smallpages only virtio_blk only qcow2 +#only raw only bridge only virtio_net only no_virtio_rng diff --git a/KVM/qemu/apic_timer_v.cfg b/KVM/qemu/apic_timer_v.cfg index 83362309..b7f1b699 100644 --- a/KVM/qemu/apic_timer_v.cfg +++ b/KVM/qemu/apic_timer_v.cfg @@ -1,5 +1,7 @@ - apic_timer_v: type = apic_timer_v + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" vms = "" auto_cpu_policy = "virttest" # MSR_IA32_VMX_PROCBASED_CTLS3 (0x492) VMX_FEATURE_GUEST_APIC_TIMER feature flag (bit8) diff --git a/KVM/qemu/boot_check.cfg b/KVM/qemu/boot_check.cfg index 43e9cbe5..b80dbcd8 100644 --- a/KVM/qemu/boot_check.cfg +++ b/KVM/qemu/boot_check.cfg @@ -2,12 +2,10 @@ type = boot_check virt_test_type = qemu vm_accelerator = kvm - # Don't create/remove guest images - force_create_image = no - remove_image = no start_vm = no # Stop VM after testing kill_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. shell_prompt = "^\[.*\][\#\$]\s*$" vga = std variants: @@ -33,6 +31,13 @@ - 1G: no 208_cpu, 256_cpu, 288_cpu mem = 1024 + - 2031M: + # 2031 is not aligned to the guest memory block size, so the + # in-guest total memory can not match it exactly. Only require + # the guest to boot up successfully with this memory size. + only 1_cpu + mem = 2031 + check_mem = no - 4G: mem = 4096 - 16G: diff --git a/KVM/qemu/boot_repeat.cfg b/KVM/qemu/boot_repeat.cfg index 08ef85c5..0ce043a7 100644 --- a/KVM/qemu/boot_repeat.cfg +++ b/KVM/qemu/boot_repeat.cfg @@ -1,4 +1,6 @@ - boot_repeat: + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" variants: - one_vm_repeat: virt_test_type = qemu diff --git a/KVM/qemu/boot_vm_in_hugepage.cfg b/KVM/qemu/boot_vm_in_hugepage.cfg index 15811fda..1dbf9263 100644 --- a/KVM/qemu/boot_vm_in_hugepage.cfg +++ b/KVM/qemu/boot_vm_in_hugepage.cfg @@ -2,6 +2,8 @@ virt_test_type = qemu type = boot kill_vm_on_error = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" login_timeout = 240 setup_hugepages = yes pre_command_noncritical = yes diff --git a/KVM/qemu/boot_win11.cfg b/KVM/qemu/boot_win11.cfg new file mode 100644 index 00000000..5c8a7c1a --- /dev/null +++ b/KVM/qemu/boot_win11.cfg @@ -0,0 +1,19 @@ +- boot_win11: + type = boot_win11 + # In this testcase you should change image_name & image format into correct format for windows guest. + # Also you should change `display` parameter among (vnc, sdl, spice) according to your machine config + # and comment `display` parameter in your avocado config file. + display = "vnc" + # Finally comment `only RHEL.9` and `only qcow2`, so as other similar parameter which limits + # host os_variant or image type, add following contents to your avocado config file: + # only Windows + # only Win10 + # only raw + virt_test_type = qemu + vm_accelerator = kvm + start_vm = no + # Stop VM after testing + kill_vm = yes + vga = std + auto_cpu_model = "no" + cpu_model = host diff --git a/KVM/qemu/buslock_de.cfg b/KVM/qemu/buslock_de.cfg index 90b95710..8ff5bedb 100644 --- a/KVM/qemu/buslock_de.cfg +++ b/KVM/qemu/buslock_de.cfg @@ -3,10 +3,10 @@ only Linux virt_test_type = qemu vm_accelerator = kvm - force_create_image = no - remove_image = no start_vm = yes kill_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" auto_cpu_model = "no" cpu_model = host vga = std diff --git a/KVM/qemu/buslock_ve.cfg b/KVM/qemu/buslock_ve.cfg new file mode 100644 index 00000000..256f3d9a --- /dev/null +++ b/KVM/qemu/buslock_ve.cfg @@ -0,0 +1,41 @@ +- buslock_ve: + type = buslock_ve + only Linux + virt_test_type = qemu + vm_accelerator = kvm + start_vm = no + kill_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" + auto_cpu_model = "no" + cpu_model = host + vga = std + test_dir = '/tmp' + login_timeout = 240 + deps_subdir = "bus_lock" + bus_lock_common_file = bus_lock_common.h + # IA32_VMX_PROCBASED_CTLS2 MSR must have bit 62 (BUS_LOCK_DETECT) set on host. + host_msr_probe = "rdmsr 0x48b -0 --bitfield 62:62" + variants: + - buslock_ve_01: + buslock_ve_action = bus_lock_exit + machine_type_extra_params = "bus-lock-ratelimit=20" + bus_lock_source_file = bus_lock.c + bus_lock_exec_file = bus_lock.out + trace_settle_time = 3 + - buslock_ve_02: + buslock_ve_action = ratelimit_effect + bus_lock_source_file = bus_lock_ct.c + bus_lock_exec_file = bus_lock_ct.out + # Modernized: use the named split-locks PMU event instead of the + # legacy raw SQ_MISC encoding (cpu/event=0x2c,umask=0x10/), which + # no longer exists on current Intel server CPUs. bus_lock.split_locks + # counts split locks issued by any core and is available on the + # HW target used for validation. + perf_event = "bus_lock.split_locks" + perf_interval_ms = 1000 + perf_interval_count = 2 + # Two runs: baseline (no ratelimit) then bus-lock-ratelimit=20. + ratelimit_runs = "baseline ratelimited" + machine_extra_baseline = "" + machine_extra_ratelimited = "bus-lock-ratelimit=20" diff --git a/KVM/qemu/cache_model.cfg b/KVM/qemu/cache_model.cfg new file mode 100644 index 00000000..6535eea8 --- /dev/null +++ b/KVM/qemu/cache_model.cfg @@ -0,0 +1,38 @@ +- cache_model: + type = cache_model + virt_test_type = qemu + vm_accelerator = kvm + force_create_image = no + remove_image = no + image_snapshot = yes + start_vm = no + kill_vm = yes + shell_prompt = "^\[.*\][\#\$]\s*$" + vga = std + auto_cpu_model = no + # avocado-vt has no 'modules' knob. It builds -smp from + # sockets/dies/cores/threads only. We provide the module axis via + # extra_params '-smp modules=2', which QEMU merges into the same + # -smp option, so the final product is: + # sockets(2) * dies(1) * modules(2) * cores(16) * threads(2) = 128 + smp = 128 + vcpu_maxcpus = 128 + vcpu_sockets = 2 + vcpu_cores = 16 + vcpu_threads = 2 + extra_params += " -smp modules=2" + # Guest package that provides the `cpuid` tool (used for CPUID.4 check) + cpuid_pkg = cpuid + variants: + - srf: + cpu_model = SierraForest-v6 + # Expected CPUID.4 sharing / associativity for cache 0..3 + # (L1d, L1i, L2, L3). Format: 8 space-separated hex values: + # c0_sharing c0_assoc c1_sharing c1_assoc c2_sharing c2_assoc c3_sharing c3_assoc + cpuid_cache_expect = "0x1 0x8 0x1 0x8 0x1f 0x10 0x3f 0xc" + - gnr: + cpu_model = GraniteRapids-v7 + cpuid_cache_expect = "0x1 0xc 0x1 0x10 0x1 0x10 0x3f 0x10" + - spr: + cpu_model = SapphireRapids-v8 + cpuid_cache_expect = "0x1 0xc 0x1 0x8 0x1 0x10 0x3f 0xf" diff --git a/KVM/qemu/cpu_aia.cfg b/KVM/qemu/cpu_aia.cfg new file mode 100644 index 00000000..c5aa26b0 --- /dev/null +++ b/KVM/qemu/cpu_aia.cfg @@ -0,0 +1,19 @@ +- cpu_aia: + type = cpu_aia + virt_test_type = qemu + vm_accelerator = kvm + start_vm = no + # Stop VM after testing + kill_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" + vga = std + auto_cpu_model = "no" + cpu_model = host + variants: + - waitpkg: + test_command = "tpause umwait umonitor" + - movdir64b: + test_command = "movdir64b" + - movdiri: + test_command = "movdiri" diff --git a/KVM/qemu/cpu_model.cfg b/KVM/qemu/cpu_model.cfg index 177f5624..44d1ca30 100644 --- a/KVM/qemu/cpu_model.cfg +++ b/KVM/qemu/cpu_model.cfg @@ -2,15 +2,13 @@ type = cpu_model virt_test_type = qemu vm_accelerator = kvm - # Don't create/remove guest images - force_create_image = no - remove_image = no start_vm = no # Stop VM after testing kill_vm = yes auto_cpu_model = no cpu_model = "" default_cpu_model = host + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. shell_prompt = "^\[.*\][\#\$]\s*$" vga = std test_dir = '/home' @@ -23,9 +21,9 @@ enable_check = no - dmr: # To-add: APX - cpu_model = DiamondRapids-v1 + cpu_model = DiamondRapids-v2 check_host_cpuid = yes - features = "AMX_COMPLEX AMX_FP8 AMX_TF32 AMX_AVX512 AMX_MOVRS MOVRS AVX10" + features = "AMX_COMPLEX AMX_FP8 AMX_AVX512 AMX_MOVRS MOVRS AVX10" cpuid = "24 0 0 0 b 16, 24 0 0 0 b 17, 24 0 0 0 b 18, 24 0 1 0 c 2, 24 0 0 0 b 0:7 2" - cwf: # To-add: NMIS @@ -33,20 +31,54 @@ check_host_cpuid = yes features = "SHA512 SM3 SM4 AVX_VNNI_INT16 FRED LKGS" - srf: - cpu_model = SierraForest-v3 + cpu_model = SierraForest-v6 check_host_cpuid = yes features = "AVX_IFMA AVX_VNNI_INT8 AVX_NE_CONVERT CMPCCXADD LAM LASS" + - srf_vmx: + cpu_model = SierraForest-v6 + cpu_model_flags = ",+vmx" + check_host_cpuid = yes + features = "VMX" - gnr: - cpu_model = GraniteRapids-v3 + cpu_model = GraniteRapids-v7 check_host_cpuid = yes features = "AMX_FP16 PREFETCHI AVX10_1" cpuid = "24 0 0 0 b 16 1, 24 0 0 0 b 17 1, 24 0 0 0 b 18 1, 24 0 0 0 b 0:7 1" + - gnr_vmx: + cpu_model = GraniteRapids-v7 + cpu_model_flags = ",+vmx" + check_host_cpuid = yes + features = "VMX" - spr: - cpu_model = SapphireRapids-v4 + cpu_model = SapphireRapids-v8 check_host_cpuid = yes - features = "AVX512_FP16 SERIALIZE TSXLDTRK AVX_VNNI AMX_BF16 AMX_INT8 AMX_TILE XFD MOVDIRI MOVDIR64B WAITPKG CLDEMOTE" + features = "AVX512_FP16 SERIALIZE TSXLDTRK AVX_VNNI AMX_BF16 AMX_INT8 AMX_TILE XFD MOVDIRI MOVDIR64B CLDEMOTE" cpuid = "1d 0 0 0 a 0:31 1, 1d 0 1 0 a 0:15 8192, 1d 0 1 0 a 16:31 1024, 1d 0 1 0 b 0:15 64, 1d 0 1 0 b 16:31 8, 1d 0 1 0 c 0:15 16, 1e 0 0 0 b 0:7 16, 1e 0 0 0 b 8:23 64, d 0 0x11 0 a 0:31 64, d 0 0x11 0 b 0:31 2752, d 0 0x11 0 c 0:0 0, d 0 0x11 0 c 1, d 0 0x11 0 c 2:2 0, d 0 0x12 0 a 0:31 8192, d 0 0x12 0 b 0:31 2816, d 0 0x12 0 c 0:0 0, d 0 0x12 0 c 1, d 0 0x12 0 c 2" + - spr_vmx: + cpu_model = SapphireRapids-v8 + cpu_model_flags = ",+vmx" + check_host_cpuid = yes + features = "VMX" + - cooperlake_vmx: + cpu_model = Cooperlake-v2 + cpu_model_flags = ",+vmx" + check_host_cpuid = yes + features = "VMX" - icx: - cpu_model = Icelake-Server-v7 + cpu_model = Icelake-Server-v9 check_host_cpuid = yes features = "UMIP GFNI VAES VPCLMULQDQ SHA_NI 5LP AVX512_IFMA AVX512_VBMI AVX512_VBMI2 AVX512_VNNI AVX512_BITALG" + - broadwell: + cpu_model = Broadwell-v4 + check_host_cpuid = yes + features = "ADX" + - cascadelake: + cpu_model = Cascadelake-Server-v7 + cpu_model_flags = ",+vmx" + check_host_cpuid = yes + features = "AVX512_VNNI PKU ARCH_CAPABILITIES IBRS SSBD VMX" + cpuid = "1 0 0 0 a 0:3 6" + - skylake_server: + cpu_model = Skylake-Server-v6 + check_host_cpuid = yes + features = "AVX512_F" diff --git a/KVM/qemu/cpu_pku.cfg b/KVM/qemu/cpu_pku.cfg index a7e4141a..5246aec0 100644 --- a/KVM/qemu/cpu_pku.cfg +++ b/KVM/qemu/cpu_pku.cfg @@ -3,6 +3,8 @@ only i386 x86_64 no RHEL.6 RHEL.7 RHEL.8.0 RHEL.8.1 RHEL.8.2 RHEL.8.3 RHEL.8.4 RHEL.8.5 RHEL.8.6 start_vm = no + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" image_snapshot = yes timeout = 120 unsupported_models = "EPYC-Rome EPYC EPYC-IBPB Opteron_G5 Opteron_G4 Opteron_G3 Opteron_G2 Opteron_G1" diff --git a/KVM/qemu/cpuid_1f.cfg b/KVM/qemu/cpuid_1f.cfg new file mode 100644 index 00000000..42089f9f --- /dev/null +++ b/KVM/qemu/cpuid_1f.cfg @@ -0,0 +1,36 @@ +- cpuid_1f: + type = cpuid_1f + kill_vm_on_error = yes + start_vm = no + auto_cpu_model = "no" + cpu_model = host + shell_prompt = "^\[.*\][\#\$]\s*$" + vga = std + test_dir = '/home' + source_file = 'cpuid_1f_check.c' + exec_file = 'cpuid_1f_check' + smp = 8 + variants: + - host: + host_only = yes + - srf: + cpu_model = SierraForest-v6 + cpu_model_flags = ",pmu=off" + vcpu_sockets = 2 + vcpu_dies = 2 + vcpu_cores = 2 + vcpu_threads = 1 + - gnr: + cpu_model = GraniteRapids-v7 + cpu_model_flags = ",pmu=off" + vcpu_sockets = 2 + vcpu_dies = 2 + vcpu_cores = 2 + vcpu_threads = 1 + - spr: + cpu_model = SapphireRapids-v8 + cpu_model_flags = ",pmu=off" + vcpu_sockets = 2 + vcpu_dies = 2 + vcpu_cores = 2 + vcpu_threads = 1 diff --git a/KVM/qemu/cpuid_extended.cfg b/KVM/qemu/cpuid_extended.cfg new file mode 100644 index 00000000..bcfb1fa4 --- /dev/null +++ b/KVM/qemu/cpuid_extended.cfg @@ -0,0 +1,26 @@ +- cpuid_extended: + type = cpuid_extended + virt_test_type = qemu + vm_accelerator = kvm + force_create_image = no + remove_image = no + image_snapshot = yes + start_vm = no + kill_vm = yes + shell_prompt = "^\[.*\][\#\$]\s*$" + vga = std + auto_cpu_model = no + cpu_model = host + variants: + - leaf_80000005: + cpuid_leaf = "0x80000005" + expected_eax = 0 + expected_ebx = 0 + expected_ecx = 0 + expected_edx = 0 + - leaf_80000006: + cpuid_leaf = "0x80000006" + expected_eax = 0 + expected_ebx = 0 + expected_ecx = 0 + expected_edx = 0 diff --git a/KVM/qemu/deps/aia/Makefile b/KVM/qemu/deps/aia/Makefile new file mode 100755 index 00000000..88ede6f2 --- /dev/null +++ b/KVM/qemu/deps/aia/Makefile @@ -0,0 +1,3 @@ +test: +# gcc libaia.c -c -g -o libaia.o + gcc test.c -g -o test diff --git a/KVM/qemu/deps/aia/libaia.h b/KVM/qemu/deps/aia/libaia.h new file mode 100755 index 00000000..ae596323 --- /dev/null +++ b/KVM/qemu/deps/aia/libaia.h @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2026 Intel Corporation. All rights reserved. */ +/* + * This test case checks movdir64b, movdiri and waitpkg features. + */ + +#ifndef LIBAIA_H +#define LIBAIA_H + +#include + +/* CPUID.07H.0H:ECX.DIRSTR[bit 27] */ +#define MOVDIRI_BIT 27 +/* CPUID.07H.0H:ECX.DIRSTR64B[bit 28] */ +#define MOVDIR64B_BIT 28 + +/* CPUID.07H:ECX[5] */ +#define UMONITOR_UMWAIT_BIT 5 + +/* + * Move 32-bit data into memory using direct store. + */ +void movdir32(int *dst, int data) +{ + /* + * According to calling convention for i386 and AMD64 Linux + * user-level applications, p is stored in edi and a is stored in esi. + * So the direct memory move instruction is: + * movdiri esi, [edi] + */ + asm volatile(".byte 0x48, 0x0f, 0x38, 0xf9, 0x02" + : + : "a" (data), "d" (dst)); +} + +/* + * Move 64 bytes from memory to memory as direct store. + */ +void movdir64b(char *dst, char *src) +{ + /* + * According to calling convention for AMD64 Linux + * user-level applications, p is stored in rdi and a is stored in rsi. + * So the direct 64 bytes direct store instruction is: + * movdir64b [rsi], rdi + */ + asm volatile(".byte 0x66, 0x0f, 0x38, 0xf8, 0x02" + : + : "a" (src), "d" (dst)); +} + +inline void cpuid(void) +{ + int eax, ebx, ecx, edx; + + asm volatile("mov $5, %%eax\t\n" + "cpuid\t\n" + : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) + :); +} + +bool movdiri_supported(void) +{ + int eax, ebx, ecx, edx; + + asm volatile("mov $7, %%eax\t\n" + "mov $0, %%ecx\t\n" + "cpuid\t\n" + : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)); + + return ecx & (1 << MOVDIRI_BIT); +} + +unsigned long tsc_freq; + +bool get_tsc_freq(void) +{ + int eax, ebx, ecx, edx; + + asm volatile("mov $15, %%eax\t\n" + "cpuid\t\n" + : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)); + if (ebx == 0 || ecx == 0) + return -1; + + tsc_freq = ecx * ebx / eax; + + return 0; +} + +int nsec_to_tsc(unsigned long nsec, unsigned long *tsc) +{ + if (tsc_freq <= 0) + return -1; + + *tsc = tsc_freq * nsec / 1000000000; + + return 0; +} + +bool movdir64b_supported(void) +{ + int eax, ebx, ecx, edx; + + asm volatile("mov $7, %%eax\t\n" + "mov $0, %%ecx\t\n" + "cpuid\t\n" + : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)); + + return ecx & (1 << MOVDIR64B_BIT); +} + +bool umonitor_umwait_supported(void) +{ + int eax, ebx, ecx, edx; + + asm volatile("mov $7, %%eax\t\n" + "mov $0, %%ecx\t\n" + "cpuid\t\n" + : "=a"(eax), "=b" (ebx), "=c" (ecx), "=d" (edx)); + + return ecx & (1 << UMONITOR_UMWAIT_BIT); +} + +void umonitor(char *addr) +{ + asm volatile(".byte 0xf3, 0x0f, 0xae, 0xf7\t\n" + : + : "rdi" (addr)); +} + +void _umwait(int state, unsigned long eax, unsigned long edx) +{ + asm volatile("mov %%esi, %%eax\t\n" + ".byte 0xf2, 0x0f, 0xae, 0xf7\t\n" + : + :); +} + +void umwait(int state, unsigned long nsec) +{ + unsigned long tsc; + int err; + + err = nsec_to_tsc(nsec, &tsc); + if (err) + tsc = 0; + + _umwait(state, tsc >> 32, tsc & 0xffffffff); +} + +void _tpause(int state, unsigned long eax, unsigned long edx) +{ + asm volatile("mov %%esi, %%eax\n\t" + ".byte 0x66, 0x0f, 0xae, 0xf7\n\t" + : + :); +} + +void tpause(int state, unsigned long nsec) +{ + unsigned long tsc; + int err; + + err = nsec_to_tsc(nsec, &tsc); + if (err) + tsc = 0; + + _tpause(state, tsc >> 32, tsc & 0xffffffff); +} + +#endif diff --git a/KVM/qemu/deps/aia/test.c b/KVM/qemu/deps/aia/test.c new file mode 100755 index 00000000..cbb5bec8 --- /dev/null +++ b/KVM/qemu/deps/aia/test.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2026 Intel Corporation. All rights reserved. */ +/* + * This test case checks movdir64b, movdiri and waitpkg features. + */ + +#include +#include +#include + +#include "libaia.h" + +void test_movdiri(void) +{ + int dst[10] __attribute((aligned(64))); + + __attribute((aligned(64))) int data; + + if (!movdiri_supported()) { + printf("movdiri is not supported\n"); + + return; + } + dst[0] = 0; + data = 1; + + movdir32(dst, data); + + printf("movdiri test passed\n"); +} + +void test_movdir64b(void) +{ + char src[1024], dst[1024] __attribute((aligned(64))); + + if (!movdir64b_supported()) { + printf("movdir64b is not supported\n"); + + return; + } + memset(src, 0, 1024); + memset(dst, 0, 1024); + strcpy(src, "testdata"); + +// printf("before movdir64: src=%s dst=%s\n", src, dst); + movdir64b(src, dst); +// printf("after movdir64:\n"); +// printf("%s, %s\n", src, dst); + if (strcmp(src, dst)) + printf("movedir64b test failed\n"); + else + printf("movedir64b test passed\n"); +} + +void test_tpause(void) +{ + if (umonitor_umwait_supported()) { + tpause(1, 1000); + printf("tpause is supported and test passed\n"); + } else { + printf("tpause is not supported\n"); + } +} + +void test_umonitor(void) +{ + char p[1024] __attribute((aligned(64))); + + if (umonitor_umwait_supported()) { + umonitor(p); + printf("umonitor pass\n"); + } else { + printf("umonitor not supported\n"); + } +} + +void test_umwait(void) +{ + if (umonitor_umwait_supported()) { + umwait(1, 1000); + printf("umwait is supported and test passed\n"); + } else { + printf("umwait is not supported\n"); + } +} + +int main(int argc, char *argv[]) +{ +// tsc(); +//return; + int i = 1; + + while (argc > i) { + if (strcmp(argv[i], "movdiri") == 0) + test_movdiri(); + else if (strcmp(argv[i], "movdir64b") == 0) + test_movdir64b(); + else if (strcmp(argv[i], "tpause") == 0) + test_tpause(); + else if (strcmp(argv[i], "umwait") == 0) + test_umwait(); + else if (strcmp(argv[i], "umonitor") == 0) + test_umonitor(); + i++; + } + return 0; +} diff --git a/KVM/qemu/deps/bus_lock/bus_lock_ct.c b/KVM/qemu/deps/bus_lock/bus_lock_ct.c new file mode 100644 index 00000000..715e50d4 --- /dev/null +++ b/KVM/qemu/deps/bus_lock/bus_lock_ct.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2026 Intel Corporation + +#include +#include + +#include "bus_lock_common.h" + +int main(void) +{ + unsigned char *buffer; + int *int_ptr; + int cache_line_size; + + cache_line_size = get_cache_line_size_cpuid(); + printf("The cache line size is %d bytes.\n", cache_line_size); + + buffer = (unsigned char *)aligned_alloc(cache_line_size, + 2 * cache_line_size); + int_ptr = (int *)(buffer + cache_line_size - 1); + + while (1) + locked_add_1(int_ptr); + + return 0; +} diff --git a/KVM/qemu/deps/cpuid_1f/cpuid_1f_check.c b/KVM/qemu/deps/cpuid_1f/cpuid_1f_check.c new file mode 100644 index 00000000..4a7b54c4 --- /dev/null +++ b/KVM/qemu/deps/cpuid_1f/cpuid_1f_check.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2026 Intel Corporation + +/* + * cpuid_1f_check.c - Validate CPUID leaf 0x1F (V2 Extended Topology Enumeration) + * + * Uses the highest valid CPUID 0x1F sub-leaf to get logical CPUs per package, + * then checks it is consistent with sysconf(_SC_NPROCESSORS_CONF). + */ + +#include +#include + +static inline void cpuid(unsigned int leaf, unsigned int sub_leaf, + unsigned int *eax, unsigned int *ebx, + unsigned int *ecx, unsigned int *edx) +{ + __asm__ __volatile__("cpuid" + : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) + : "a"(leaf), "c"(sub_leaf)); +} + +int main(void) +{ + unsigned int eax, ebx, ecx, edx; + unsigned int sub_leaf = 0; + unsigned int cpus_per_pkg = 0; + int found_valid = 0; + + while (1) { + cpuid(0x1f, sub_leaf, &eax, &ebx, &ecx, &edx); + + /* ECX[15:08]: Level type. 0 means invalid - end of enumeration */ + unsigned int level_type = (ecx >> 8) & 0xff; + + if (level_type == 0) + break; + + found_valid = 1; + /* + * EBX[15:0]: Number of logical processors within the next + * higher-scoped domain. The highest valid level gives + * logical CPUs per package. + */ + cpus_per_pkg = ebx & 0xffff; + sub_leaf++; + } + + if (!found_valid) { + printf("FAIL: CPUID leaf 0x1F not supported (no valid sub-leaves)\n"); + return 1; + } + + long total_cpus = sysconf(_SC_NPROCESSORS_CONF); + + if (total_cpus < 0) { + printf("FAIL: sysconf(_SC_NPROCESSORS_CONF) failed\n"); + return 1; + } + + if (cpus_per_pkg == 0) { + printf("FAIL: CPUID 0x1F reported 0 CPUs per package\n"); + return 1; + } + + /* + * Verify topology consistency: total CPUs should be evenly + * divisible by CPUs-per-package + */ + if ((unsigned int)total_cpus % cpus_per_pkg != 0) { + printf("FAIL: Topology inconsistent: total %ld CPUs not divisible by %u CPUs/pkg\n", + total_cpus, cpus_per_pkg); + return 1; + } + + unsigned int num_pkgs = (unsigned int)total_cpus / cpus_per_pkg; + + printf("PASS: CPUID 0x1F topology consistent: %u CPUs/pkg * %u pkg(s) = %ld total\n", + cpus_per_pkg, num_pkgs, total_cpus); + return 0; +} diff --git a/KVM/qemu/deps/nested_l2_test/pku_test.c b/KVM/qemu/deps/nested_l2_test/pku_test.c new file mode 100644 index 00000000..38679e63 --- /dev/null +++ b/KVM/qemu/deps/nested_l2_test/pku_test.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2025 Intel Corporation +// +// PKU (Protection Keys for Userspace) validation test. +// Tests rdpkru/wrpkru, pkey_alloc, pkey_mprotect, and enforcement. +// Compile: gcc -o pku_test pku_test.c + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +static sigjmp_buf jmpbuf; +static sig_atomic_t got_sigsegv; + +static inline unsigned int rdpkru(void) +{ + unsigned int eax, edx, ecx = 0; + + asm volatile(".byte 0x0f,0x01,0xee" + : "=a"(eax), "=d"(edx) + : "c"(ecx)); + return eax; +} + +static inline void wrpkru(unsigned int pkru) +{ + unsigned int ecx = 0, edx = 0; + + asm volatile(".byte 0x0f,0x01,0xef" + : : "a"(pkru), "c"(ecx), "d"(edx)); +} + +static void sigsegv_handler(int sig) +{ + (void)sig; + got_sigsegv = 1; + siglongjmp(jmpbuf, 1); +} + +int main(void) +{ + unsigned int pkru; + void *ptr; + int pkey; + struct sigaction sa; + + /* Test 1: rdpkru/wrpkru basic operation */ + wrpkru(0x55555554); + pkru = rdpkru(); + if (pkru != 0x55555554) { + printf("FAIL: wrpkru/rdpkru mismatch: got 0x%08x\n", pkru); + return 1; + } + printf("PASS: wrpkru/rdpkru works\n"); + wrpkru(0x0); + + /* Test 2: pkey_alloc */ + pkey = pkey_alloc(0, 0); + if (pkey < 0) { + printf("FAIL: pkey_alloc\n"); + return 1; + } + printf("PASS: pkey_alloc ok (pkey=%d)\n", pkey); + + /* Test 3: pkey_mprotect */ + ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (ptr == MAP_FAILED) { + printf("FAIL: mmap\n"); + return 1; + } + if (pkey_mprotect(ptr, 4096, PROT_READ | PROT_WRITE, pkey) != 0) { + printf("FAIL: pkey_mprotect\n"); + return 1; + } + printf("PASS: pkey_mprotect ok\n"); + + /* Write data while access is permitted */ + *((int *)ptr) = 0xdeadbeef; + + /* Test 4: PKU enforcement - disable write access via PKRU */ + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sigsegv_handler; + sigemptyset(&sa.sa_mask); + sigaction(SIGSEGV, &sa, NULL); + + /* Set AD (access disable) bit for this pkey: bit (2*pkey) */ + got_sigsegv = 0; + wrpkru(1U << (2 * pkey + 1)); /* WD bit = write disable */ + + if (sigsetjmp(jmpbuf, 1) == 0) { + /* Attempt write to pkey-protected page - should fault */ + *((int *)ptr) = 0x12345678; + /* If we reach here, enforcement failed */ + wrpkru(0x0); + printf("FAIL: write to WD-protected page did not fault\n"); + return 1; + } + + /* Restore PKRU and verify we caught the fault */ + wrpkru(0x0); + if (!got_sigsegv) { + printf("FAIL: did not receive SIGSEGV\n"); + return 1; + } + printf("PASS: PKU write-disable enforcement works (SIGSEGV caught)\n"); + + /* Test 5: access disable */ + got_sigsegv = 0; + wrpkru(1U << (2 * pkey)); /* AD bit = access disable */ + + if (sigsetjmp(jmpbuf, 1) == 0) { + /* Attempt read from pkey-protected page - should fault */ + int tmp = *((int *)ptr); + (void)tmp; + wrpkru(0x0); + printf("FAIL: read from AD-protected page did not fault\n"); + return 1; + } + + wrpkru(0x0); + if (!got_sigsegv) { + printf("FAIL: did not receive SIGSEGV on read\n"); + return 1; + } + printf("PASS: PKU access-disable enforcement works (SIGSEGV caught)\n"); + + pkey_free(pkey); + munmap(ptr, 4096); + + printf("all tests OK\n"); + return 0; +} diff --git a/KVM/qemu/deps/ras/victim.c b/KVM/qemu/deps/ras/victim.c index d2b81848..37886807 100755 --- a/KVM/qemu/deps/ras/victim.c +++ b/KVM/qemu/deps/ras/victim.c @@ -11,7 +11,9 @@ * Gong Chen * Wen Jin * - * Copy from mce-test + * Vendored from the upstream Intel mce-test project: + * https://git.kernel.org/pub/scm/utils/cpu/mce/mce-test.git + * tools/hwpoison/victim.c */ #include diff --git a/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_back_feature_off.h b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_back_feature_off.h new file mode 100644 index 00000000..fcde10fb --- /dev/null +++ b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_back_feature_off.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include "tdx-compliance.h" + +#define DEF_CPUID_TEST(_leaf, _subleaf) \ +{ \ + .name = "CPUID_" #_leaf "_" #_subleaf, \ + .leaf = _leaf, \ + .subleaf = _subleaf, \ +} + +#define EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + int bnr = _bit_nr; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_nr "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = BIT(bnr); \ + t->regs._reg.expect = BIT(bnr) * (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BIT(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn) \ + EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, 0xbad, 0xbad) + +#define EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg; \ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = 0xffffffff; \ + t->regs._reg.expect = (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BYTE(_leaf, _subleaf, _reg, _val, _vsn) \ + EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, 0xbad, 0xbad) +#define EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, _td_ctl, _pv_ctl) do {\ + int i = 0; \ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_e ":" #_bit_s "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + for (i = _bit_s; i <= (_bit_e); i++) { \ + t->regs._reg.mask |= BIT(i); \ + } \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_RES_BITS(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn) \ + EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, 0xbad, 0xbad) + +#ifdef AUTOGEN_CPUID +void initial_cpuid(void); +#else +void initial_cpuid(void) +{ +/********* The following test cases are defined for #VE reduction. *********/ + +/* + * There are 3 configurations in total: + * 1. By default -- when #VE Reduction is enabled: TDCS.TD_CTRL.REDUCE_VE ==1 , + * TDCS.FEATURE_PARAVIRT_CTLS is all-0. + * 2. TD_CTLS.REDUCE_VE == 1, FEATURE_PARAVIRT_CTLS == 1 + * 3. Backward-Compatible -- when #VE reduction is not enabled: TD_CTLS.REDUCE_VE is 0. + */ + +/* 3. Backward-Compatible -- when #VE reduction is not enabled: TD_CTLS.REDUCE_VE is 0. */ +// /* CPUID(0x1) */ +// /* EST(est) */ + EXP_CPUID_BIT_CTL(0x1, 0, ecx, 7, 0x0, VER1_5, 0, 0); //not supported by VMM +// /* TSC_DEADLINE(tsc-deadline) */ +// EXP_CPUID_BIT_CTL(0x1, 0, ecx, 24, 0x1, VER1_5, 0, 0); //enable it by qemu + EXP_CPUID_BIT_CTL(0x1, 0, ecx, 24, 0x0, VER1_5, 0, 0); //disable it by qemu +// /* MCA(mce) */ +// EXP_CPUID_BIT_CTL(0x1, 0, edx, 7, 0x1, VER1_5, 0, 0); //enable it by qemu + EXP_CPUID_BIT_CTL(0x1, 0, edx, 7, 0x1, VER1_5, 0, 0); //disable it by qemu +// /* MTRR(mtrr) */ +// EXP_CPUID_BIT_CTL(0x1, 0, edx, 12, 0x1, VER1_5, 0, 0); //enable it by qemu + EXP_CPUID_BIT_CTL(0x1, 0, edx, 12, 0x1, VER1_5, 0, 0); //disable it by qemu +// /* MCA(mca) */ +// EXP_CPUID_BIT_CTL(0x1, 0, edx, 14, 0x1, VER1_5, 0, 0); //enable it by qemu + EXP_CPUID_BIT_CTL(0x1, 0, edx, 14, 0x1, VER1_5, 0, 0); //disable it by qemu +// /* TM(acpi) */ + EXP_CPUID_BIT_CTL(0x1, 0, edx, 22, 0x0, VER1_5, 0, 0); //not supported by VMM +// +// /* CPUID(0x2) */ +// EXP_CPUID_BYTE_CTL(0x2, 0, eax, 0, VER1_5, 0, 0); //trigger #VE +// +// /* CPUID(0x6) */ +// EXP_CPUID_BIT_CTL(0x6, 0, eax, 2, 0x0, VER1_5, 0, 0);//trigger #VE +// +// /* CPUID(0x7) */ +// /* CORE_CAPABILITIES(core-capability) */ +// EXP_CPUID_BIT_CTL(0x7, 0, edx, 30, 0x1, VER1_5, 0, 0); //enable it by qemu + EXP_CPUID_BIT_CTL(0x7, 0, edx, 30, 0x1, VER1_5, 0, 0); //disable it by qemu +// /* RDT_M(pqm) */ + EXP_CPUID_BIT_CTL(0x7, 0, ebx, 12, 0x0, VER1_5, 0, 0); //not supported by qemu +// /* RDT_A(rdta) */ + EXP_CPUID_BIT_CTL(0x7, 0, ebx, 15, 0x0, VER1_5, 0, 0); //not supported by qemu +// /* PCONFIG(pconfig) */ + EXP_CPUID_BIT_CTL(0x7, 0, edx, 18, 0x0, VER1_5, 0, 0); //not supported by qemu +// /* TME(tme) */ + EXP_CPUID_BIT_CTL(0x7, 0, ecx, 13, 0x0, VER1_5, 0, 0); //not supported by qemu +// +// /* CPUID(0x9), enumerated by virtual CPUID(1).ECX[18] */ +// /* DCA(dca) */ + EXP_CPUID_BIT_CTL(0x1, 0, ecx, 18, 0x0, VER1_5, 0, 0); //not supported by VMM +// EXP_CPUID_BYTE_CTL(0x9, 0, eax, 0x0, VER1_5, 0, 0); //trigger #VE +// +// /* CPUID(0xb), Per SDM, CPUID(0x1F, *) is a preferred superset to leaf CPUID(0xB,*) */ +// EXP_CPUID_BYTE_CTL(0xb, 0, eax, 0, VER1_5, 0, 0); //trigger #VE +// +// /* CPUID(0xc) Reserved */ +// EXP_CPUID_BYTE_CTL(0xc, 0, eax, 0, VER1_5, 0, 0); //trigger #VE +// +} +#endif diff --git a/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_back_feature_on.h b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_back_feature_on.h new file mode 100644 index 00000000..4c7bca13 --- /dev/null +++ b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_back_feature_on.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include "tdx-compliance.h" + +#define DEF_CPUID_TEST(_leaf, _subleaf) \ +{ \ + .name = "CPUID_" #_leaf "_" #_subleaf, \ + .leaf = _leaf, \ + .subleaf = _subleaf, \ +} + +#define EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + int bnr = _bit_nr; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_nr "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = BIT(bnr); \ + t->regs._reg.expect = BIT(bnr) * (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BIT(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn) \ + EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, 0xbad, 0xbad) + +#define EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg; \ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = 0xffffffff; \ + t->regs._reg.expect = (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BYTE(_leaf, _subleaf, _reg, _val, _vsn) \ + EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, 0xbad, 0xbad) +#define EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, _td_ctl, _pv_ctl) do {\ + int i = 0; \ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_e ":" #_bit_s "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + for (i = _bit_s; i <= (_bit_e); i++) { \ + t->regs._reg.mask |= BIT(i); \ + } \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_RES_BITS(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn) \ + EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, 0xbad, 0xbad) + +#ifdef AUTOGEN_CPUID +void initial_cpuid(void); +#else +void initial_cpuid(void) +{ +/********* The following test cases are defined for #VE reduction. *********/ + +/* + * There are 3 configurations in total: + * 1. By default -- when #VE Reduction is enabled: TDCS.TD_CTRL.REDUCE_VE ==1 , + * TDCS.FEATURE_PARAVIRT_CTLS is all-0. + * 2. TD_CTLS.REDUCE_VE == 1, FEATURE_PARAVIRT_CTLS == 1 + * 3. Backward-Compatible -- when #VE reduction is not enabled: TD_CTLS.REDUCE_VE is 0. + */ + +/* 3. Backward-Compatible -- when #VE reduction is not enabled: TD_CTLS.REDUCE_VE is 0. */ +// /* CPUID(0x1) */ +// /* EST(est) */ +// EXP_CPUID_BIT_CTL(0x1, 0, ecx, 7, 0x0, VER1_5, 0, 0); //not supported by VMM +// /* TSC_DEADLINE(tsc-deadline) */ + EXP_CPUID_BIT_CTL(0x1, 0, ecx, 24, 0x1, VER1_5, 0, 0); //enable it by qemu +// EXP_CPUID_BIT_CTL(0x1, 0, ecx, 24, 0x0, VER1_5, 0, 0); //disable it by qemu +// /* MCA(mce) */ + EXP_CPUID_BIT_CTL(0x1, 0, edx, 7, 0x1, VER1_5, 0, 0); //enable it by qemu +// EXP_CPUID_BIT_CTL(0x1, 0, edx, 7, 0x1, VER1_5, 0, 0); //disable it by qemu +// /* MTRR(mtrr) */ + EXP_CPUID_BIT_CTL(0x1, 0, edx, 12, 0x1, VER1_5, 0, 0); //enable it by qemu +// EXP_CPUID_BIT_CTL(0x1, 0, edx, 12, 0x1, VER1_5, 0, 0); //disable it by qemu +// /* MCA(mca) */ + EXP_CPUID_BIT_CTL(0x1, 0, edx, 14, 0x1, VER1_5, 0, 0); //enable it by qemu +// EXP_CPUID_BIT_CTL(0x1, 0, edx, 14, 0x1, VER1_5, 0, 0); //disable it by qemu +// /* TM(acpi) */ +// EXP_CPUID_BIT_CTL(0x1, 0, edx, 22, 0x0, VER1_5, 0, 0); //not supported by VMM +// +// /* CPUID(0x7) */ +// /* CORE_CAPABILITIES(core-capability) */ + EXP_CPUID_BIT_CTL(0x7, 0, edx, 30, 0x1, VER1_5, 0, 0); //enable it by qemu +// EXP_CPUID_BIT_CTL(0x7, 0, edx, 30, 0x1, VER1_5, 0, 0); //disable it by qemu +// /* RDT_M(pqm) */ +// EXP_CPUID_BIT_CTL(0x7, 0, ebx, 12, 0x0, VER1_5, 0, 0); //not supported by qemu +// /* RDT_A(rdta) */ +// EXP_CPUID_BIT_CTL(0x7, 0, ebx, 15, 0x0, VER1_5, 0, 0); //not supported by qemu +// /* PCONFIG(pconfig) */ +// EXP_CPUID_BIT_CTL(0x7, 0, edx, 18, 0x0, VER1_5, 0, 0); //not supported by qemu +// /* TME(tme) */ +// EXP_CPUID_BIT_CTL(0x7, 0, ecx, 13, 0x0, VER1_5, 0, 0); //not supported by qemu +// +// /* CPUID(0x9), enumerated by virtual CPUID(1).ECX[18] */ +// /* DCA(dca) */ +// EXP_CPUID_BIT_CTL(0x1, 0, ecx, 18, 0x0, VER1_5, 0, 0); //not supported by VMM +} +#endif diff --git a/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ctls_feature_off.h b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ctls_feature_off.h new file mode 100644 index 00000000..2db02bbb --- /dev/null +++ b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ctls_feature_off.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include "tdx-compliance.h" + +#define DEF_CPUID_TEST(_leaf, _subleaf) \ +{ \ + .name = "CPUID_" #_leaf "_" #_subleaf, \ + .leaf = _leaf, \ + .subleaf = _subleaf, \ +} + +#define EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + int bnr = _bit_nr; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_nr "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = BIT(bnr); \ + t->regs._reg.expect = BIT(bnr) * (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BIT(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn) \ + EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, 0xbad, 0xbad) + +#define EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg; \ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = 0xffffffff; \ + t->regs._reg.expect = (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BYTE(_leaf, _subleaf, _reg, _val, _vsn) \ + EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, 0xbad, 0xbad) +#define EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, _td_ctl, _pv_ctl) do {\ + int i = 0; \ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_e ":" #_bit_s "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + for (i = _bit_s; i <= (_bit_e); i++) { \ + t->regs._reg.mask |= BIT(i); \ + } \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_RES_BITS(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn) \ + EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, 0xbad, 0xbad) + +#ifdef AUTOGEN_CPUID +void initial_cpuid(void); +#else +void initial_cpuid(void) +{ +/********* The following test cases are defined for #VE reduction. *********/ + +/* + * There are 3 configurations in total: + * 1. By default -- when #VE Reduction is enabled: TDCS.TD_CTRL.REDUCE_VE ==1 , + * TDCS.FEATURE_PARAVIRT_CTLS is all-0. + * 2. TD_CTLS.REDUCE_VE == 1, FEATURE_PARAVIRT_CTLS == 1 + * 3. Backward-Compatible -- when #VE reduction is not enabled: TD_CTLS.REDUCE_VE is 0. + */ + +/* 2. TD_CTLS.REDUCE_VE == 1, FEATURE_PARAVIRT_CTLS == 1 */ +// /* CPUID(0x1) */ +// /* EST(est) */ +// EXP_CPUID_BIT_CTL(0x1, 0, ecx, 7, 0x0, VER1_5, 8, BIT(2)); //not supported by VMM +// /* TSC_DEADLINE(tsc-deadline) */ +// EXP_CPUID_BIT_CTL(0x1, 0, ecx, 24, 0x1, VER1_5, 8, BIT(11)); //enable it by qemu + EXP_CPUID_BIT_CTL(0x1, 0, ecx, 24, 0x0, VER1_5, 8, BIT(11)); //disable it by qemu +// /* MCA(mce) */ +// EXP_CPUID_BIT_CTL(0x1, 0, edx, 7, 0x1, VER1_5, 8, BIT(3)); //enable it by qemu + EXP_CPUID_BIT_CTL(0x1, 0, edx, 7, 0x0, VER1_5, 8, BIT(3)); //disable it by qemu +// /* MTRR(mtrr) */ +// EXP_CPUID_BIT_CTL(0x1, 0, edx, 12, 0x1, VER1_5, 8, BIT(4)); //enable it by qemu + EXP_CPUID_BIT_CTL(0x1, 0, edx, 12, 0x0, VER1_5, 8, BIT(4)); //disable it by qemu +// /* MCA(mca) */ +// EXP_CPUID_BIT_CTL(0x1, 0, edx, 14, 0x1, VER1_5, 8, BIT(3)); //enable it by qemu + EXP_CPUID_BIT_CTL(0x1, 0, edx, 14, 0x0, VER1_5, 8, BIT(3)); //disable it by qemu +// /* TM(acpi) */ + EXP_CPUID_BIT_CTL(0x1, 0, edx, 22, 0x0, VER1_5, 8, BIT(8)); //not supported by VMM +// +// /* CPUID(0x2) */ +// EXP_CPUID_BYTE_CTL(0x2, 0, eax, 0x00feff01, VER1_5, 4, 0); +// EXP_CPUID_BYTE_CTL(0x5, 0, ebx, 0, VER1_5, 4, 0); +// EXP_CPUID_BYTE_CTL(0x2, 0, ecx, 0, VER1_5, 4, 0); +// EXP_CPUID_BYTE_CTL(0x2, 0, edx, 0, VER1_5, 4, 0); +// /* CPUID(0x7) */ +// /* CORE_CAPABILITIES(core-capability) */ +// EXP_CPUID_BIT_CTL(0x7, 0, edx, 30, 0x1, VER1_5, 8, BIT(0)); //enable it by qemu + EXP_CPUID_BIT_CTL(0x7, 0, edx, 30, 0x0, VER1_5, 8, BIT(0)); //disable it by qemu +// /* RDT_M(pqm) */ + EXP_CPUID_BIT_CTL(0x7, 0, ebx, 12, 0x0, VER1_5, 8, BIT(7)); //not supported by qemu +// /* RDT_A(rdta) */ + EXP_CPUID_BIT_CTL(0x7, 0, ebx, 15, 0x0, VER1_5, 8, BIT(6)); //not supported by qemu +// /* PCONFIG(pconfig) */ + EXP_CPUID_BIT_CTL(0x7, 0, edx, 18, 0x0, VER1_5, 8, BIT(5)); //not supported by qemu +// /* TME(tme) */ + EXP_CPUID_BIT_CTL(0x7, 0, ecx, 13, 0x0, VER1_5, 8, BIT(10)); //not supported by qemu +// +// /* CPUID(0x9), enumerated by virtual CPUID(1).ECX[18] */ +// /* DCA(dca) */ + EXP_CPUID_BIT_CTL(0x1, 0, ecx, 18, 0x0, VER1_5, 8, BIT(1)); //not supported by VMM + EXP_CPUID_BYTE_CTL(0x9, 0, eax, 0x0, VER1_5, 8, BIT(1)); //virtual CPUID(1).ECX[18] == 0 + EXP_CPUID_BYTE_CTL(0x9, 0, ebx, 0x0, VER1_5, 8, BIT(1)); //virtual CPUID(1).ECX[18] == 0 + EXP_CPUID_BYTE_CTL(0x9, 0, ecx, 0x0, VER1_5, 8, BIT(1)); //virtual CPUID(1).ECX[18] == 0 + EXP_CPUID_BYTE_CTL(0x9, 0, edx, 0x0, VER1_5, 8, BIT(1)); //virtual CPUID(1).ECX[18] == 0 + +} +#endif diff --git a/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ctls_feature_on.h b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ctls_feature_on.h new file mode 100644 index 00000000..756b96f9 --- /dev/null +++ b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ctls_feature_on.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include "tdx-compliance.h" + +#define DEF_CPUID_TEST(_leaf, _subleaf) \ +{ \ + .name = "CPUID_" #_leaf "_" #_subleaf, \ + .leaf = _leaf, \ + .subleaf = _subleaf, \ +} + +#define EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + int bnr = _bit_nr; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_nr "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = BIT(bnr); \ + t->regs._reg.expect = BIT(bnr) * (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BIT(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn) \ + EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, 0xbad, 0xbad) + +#define EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg; \ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = 0xffffffff; \ + t->regs._reg.expect = (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BYTE(_leaf, _subleaf, _reg, _val, _vsn) \ + EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, 0xbad, 0xbad) +#define EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, _td_ctl, _pv_ctl) do {\ + int i = 0; \ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_e ":" #_bit_s "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + for (i = _bit_s; i <= (_bit_e); i++) { \ + t->regs._reg.mask |= BIT(i); \ + } \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_RES_BITS(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn) \ + EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, 0xbad, 0xbad) + +#ifdef AUTOGEN_CPUID +void initial_cpuid(void); +#else +void initial_cpuid(void) +{ +/********* The following test cases are defined for #VE reduction. *********/ + +/* + * There are 3 configurations in total: + * 1. By default -- when #VE Reduction is enabled: TDCS.TD_CTRL.REDUCE_VE ==1 , + * TDCS.FEATURE_PARAVIRT_CTLS is all-0. + * 2. TD_CTLS.REDUCE_VE == 1, FEATURE_PARAVIRT_CTLS == 1 + * 3. Backward-Compatible -- when #VE reduction is not enabled: TD_CTLS.REDUCE_VE is 0. + */ + +/* 2. TD_CTLS.REDUCE_VE == 1, FEATURE_PARAVIRT_CTLS == 1 */ +// /* CPUID(0x1) */ +// /* EST(est) */ +// EXP_CPUID_BIT_CTL(0x1, 0, ecx, 7, 0x0, VER1_5, 8, BIT(2)); //not supported by VMM +// /* TSC_DEADLINE(tsc-deadline) */ + EXP_CPUID_BIT_CTL(0x1, 0, ecx, 24, 0x1, VER1_5, 8, BIT(11)); //enable it by qemu +// EXP_CPUID_BIT_CTL(0x1, 0, ecx, 24, 0x0, VER1_5, 8, BIT(11)); //disable it by qemu +// /* MCA(mce) */ + EXP_CPUID_BIT_CTL(0x1, 0, edx, 7, 0x1, VER1_5, 8, BIT(3)); //enable it by qemu +// EXP_CPUID_BIT_CTL(0x1, 0, edx, 7, 0x0, VER1_5, 8, BIT(3)); //disable it by qemu +// /* MTRR(mtrr) */ + EXP_CPUID_BIT_CTL(0x1, 0, edx, 12, 0x1, VER1_5, 8, BIT(4)); //enable it by qemu +// EXP_CPUID_BIT_CTL(0x1, 0, edx, 12, 0x0, VER1_5, 8, BIT(4)); //disable it by qemu +// /* MCA(mca) */ + EXP_CPUID_BIT_CTL(0x1, 0, edx, 14, 0x1, VER1_5, 8, BIT(3)); //enable it by qemu +// EXP_CPUID_BIT_CTL(0x1, 0, edx, 14, 0x0, VER1_5, 8, BIT(3)); //disable it by qemu +// /* TM(acpi) */ +// EXP_CPUID_BIT_CTL(0x1, 0, edx, 22, 0x0, VER1_5, 8, BIT(8)); //not supported by VMM +// +// /* CPUID(0x2) */ + EXP_CPUID_BYTE_CTL(0x2, 0, eax, 0x00feff01, VER1_5, 4, 0); + EXP_CPUID_BYTE_CTL(0x5, 0, ebx, 0, VER1_5, 4, 0); + EXP_CPUID_BYTE_CTL(0x2, 0, ecx, 0, VER1_5, 4, 0); + EXP_CPUID_BYTE_CTL(0x2, 0, edx, 0, VER1_5, 4, 0); +// /* CPUID(0x7) */ +// /* CORE_CAPABILITIES(core-capability) */ + EXP_CPUID_BIT_CTL(0x7, 0, edx, 30, 0x1, VER1_5, 8, BIT(0)); //enable it by qemu +// EXP_CPUID_BIT_CTL(0x7, 0, edx, 30, 0x0, VER1_5, 8, BIT(0)); //disable it by qemu +// /* RDT_M(pqm) */ +// EXP_CPUID_BIT_CTL(0x7, 0, ebx, 12, 0x0, VER1_5, 8, BIT(7)); //not supported by qemu +// /* RDT_A(rdta) */ +// EXP_CPUID_BIT_CTL(0x7, 0, ebx, 15, 0x0, VER1_5, 8, BIT(6)); //not supported by qemu +// /* PCONFIG(pconfig) */ +// EXP_CPUID_BIT_CTL(0x7, 0, edx, 18, 0x0, VER1_5, 8, BIT(5)); //not supported by qemu +// /* TME(tme) */ +// EXP_CPUID_BIT_CTL(0x7, 0, ecx, 13, 0x0, VER1_5, 8, BIT(10)); //not supported by qemu +// +// /* CPUID(0x9), enumerated by virtual CPUID(1).ECX[18] */ +// /* DCA(dca) */ +// EXP_CPUID_BIT_CTL(0x1, 0, ecx, 18, 0x0, VER1_5, 8, BIT(1)); //not supported by VMM +// EXP_CPUID_BYTE_CTL(0x9, 0, eax, 0x0, VER1_5, 8, BIT(1)); //virtual CPUID(1).ECX[18] == 0 +// EXP_CPUID_BYTE_CTL(0x9, 0, ebx, 0x0, VER1_5, 8, BIT(1)); //virtual CPUID(1).ECX[18] == 0 +// EXP_CPUID_BYTE_CTL(0x9, 0, ecx, 0x0, VER1_5, 8, BIT(1)); //virtual CPUID(1).ECX[18] == 0 +// EXP_CPUID_BYTE_CTL(0x9, 0, edx, 0x0, VER1_5, 8, BIT(1)); //virtual CPUID(1).ECX[18] == 0 + +} +#endif diff --git a/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_default.h b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_default.h new file mode 100644 index 00000000..d16a7a51 --- /dev/null +++ b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_default.h @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include "tdx-compliance.h" + +#define DEF_CPUID_TEST(_leaf, _subleaf) \ +{ \ + .name = "CPUID_" #_leaf "_" #_subleaf, \ + .leaf = _leaf, \ + .subleaf = _subleaf, \ +} + +#define EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + int bnr = _bit_nr; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_nr "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = BIT(bnr); \ + t->regs._reg.expect = BIT(bnr) * (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BIT(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn) \ + EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, 0xbad, 0xbad) + +#define EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg; \ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = 0xffffffff; \ + t->regs._reg.expect = (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BYTE(_leaf, _subleaf, _reg, _val, _vsn) \ + EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, 0xbad, 0xbad) +#define EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, _td_ctl, _pv_ctl) do {\ + int i = 0; \ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_e ":" #_bit_s "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + for (i = _bit_s; i <= (_bit_e); i++) { \ + t->regs._reg.mask |= BIT(i); \ + } \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_RES_BITS(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn) \ + EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, 0xbad, 0xbad) + +#ifdef AUTOGEN_CPUID +void initial_cpuid(void); +#else +void initial_cpuid(void) +{ +/********* The following test cases are defined for #VE reduction. *********/ + +/* + * There are 3 configurations in total: + * 1. By default -- when #VE Reduction is enabled: TDCS.TD_CTRL.REDUCE_VE ==1 , + * TDCS.FEATURE_PARAVIRT_CTLS is all-0. + * 2. TD_CTLS.REDUCE_VE == 1, FEATURE_PARAVIRT_CTLS == 1 + * 3. Backward-Compatible -- when #VE reduction is not enabled: TD_CTLS.REDUCE_VE is 0. + */ + +/* 1. By default, TDCS.TD_CTRL.REDUCE_VE is set, TDCS.FEATURE_PARAVIRT_CTLS is all-0 */ + + /* CPUID(0x1) */ + /* EST(est) */ + EXP_CPUID_BIT(0x1, 0, ecx, 7, 0x0, VER1_5); + /* TSC_DEADLINE(tsc-deadline) */ + EXP_CPUID_BIT(0x1, 0, ecx, 24, 0x0, VER1_5); + /* MCA(mce) */ + EXP_CPUID_BIT(0x1, 0, edx, 7, 0x0, VER1_5); + /* MTRR(mtrr) */ + EXP_CPUID_BIT(0x1, 0, edx, 12, 0x0, VER1_5); + /* MCA(mca) */ + EXP_CPUID_BIT(0x1, 0, edx, 14, 0x0, VER1_5); + /* TM(acpi) */ + EXP_CPUID_BIT(0x1, 0, edx, 22, 0x0, VER1_5); + + /* CPUID(0x2) */ + EXP_CPUID_BYTE(0x2, 0, eax, 0x00feff01, VER1_5); + EXP_CPUID_BYTE(0x2, 0, ebx, 0, VER1_5); + EXP_CPUID_BYTE(0x2, 0, ecx, 0, VER1_5); + EXP_CPUID_BYTE(0x2, 0, edx, 0, VER1_5); + + /* CPUID(0x6) */ + EXP_CPUID_BIT(0x6, 0, eax, 2, 0x1, VER1_5); + EXP_CPUID_RES_BITS(0x6, 0, eax, 0, 1, VER1_5); + EXP_CPUID_RES_BITS(0x6, 0, eax, 3, 31, VER1_5); + EXP_CPUID_BYTE(0x6, 0, ebx, 0x0, VER1_5); + EXP_CPUID_BYTE(0x6, 0, ecx, 0x0, VER1_5); + EXP_CPUID_BYTE(0x6, 0, edx, 0x0, VER1_5); + + + /* CPUID(0x7) */ + /* CORE_CAPABILITIES(core-capability) */ + EXP_CPUID_BIT(0x7, 0, edx, 30, 0x0, VER1_5); + /* RDT_M(pqm) */ + EXP_CPUID_BIT(0x7, 0, ebx, 12, 0x0, VER1_5); + /* RDT_A(rdta) */ + EXP_CPUID_BIT(0x7, 0, ebx, 15, 0x0, VER1_5); + /* PCONFIG(pconfig) */ + EXP_CPUID_BIT(0x7, 0, edx, 18, 0x0, VER1_5); + /* TME(tme) */ + EXP_CPUID_BIT(0x7, 0, ecx, 13, 0x0, VER1_5); + + /* CPUID(0x9), enumerated by virtual CPUID(1).ECX[18] */ + EXP_CPUID_BIT(0x1, 0, ecx, 18, 0x0, VER1_5); + EXP_CPUID_BYTE(0x9, 0, eax, 0x0, VER1_5); + + /* CPUID(0xc), reserved */ + EXP_CPUID_BYTE(0xc, 0, eax, 0x0, VER1_5); + EXP_CPUID_BYTE(0xc, 0, ebx, 0x0, VER1_5); + EXP_CPUID_BYTE(0xc, 0, ecx, 0x0, VER1_5); + EXP_CPUID_BYTE(0xc, 0, edx, 0x0, VER1_5); + +} +#endif diff --git a/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_tp1.h b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_tp1.h new file mode 100644 index 00000000..63525684 --- /dev/null +++ b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_tp1.h @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include "tdx-compliance.h" + +#define DEF_CPUID_TEST(_leaf, _subleaf) \ +{ \ + .name = "CPUID_" #_leaf "_" #_subleaf, \ + .leaf = _leaf, \ + .subleaf = _subleaf, \ +} + +#define EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + int bnr = _bit_nr; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_nr "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = BIT(bnr); \ + t->regs._reg.expect = BIT(bnr) * (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BIT(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn) \ + EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, 0xbad, 0xbad) + +#define EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg; \ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = 0xffffffff; \ + t->regs._reg.expect = (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BYTE(_leaf, _subleaf, _reg, _val, _vsn) \ + EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, 0xbad, 0xbad) +#define EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, _td_ctl, _pv_ctl) do {\ + int i = 0; \ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_e ":" #_bit_s "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + for (i = _bit_s; i <= (_bit_e); i++) { \ + t->regs._reg.mask |= BIT(i); \ + } \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_RES_BITS(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn) \ + EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, 0xbad, 0xbad) + +#ifdef AUTOGEN_CPUID +void initial_cpuid(void); +#else +void initial_cpuid(void) +{ +/********* The following test cases are defined for #VE reduction. *********/ + +/* + * There are 3 configurations in total: + * 1. By default -- when #VE Reduction is enabled: TDCS.TD_CTRL.REDUCE_VE ==1 , + * TDCS.FEATURE_PARAVIRT_CTLS is all-0. + * 2. TD_CTLS.REDUCE_VE == 1, FEATURE_PARAVIRT_CTLS == 1 + * 3. Backward-Compatible -- when #VE reduction is not enabled: TD_CTLS.REDUCE_VE is 0. + */ + +/* 1. By default, TDCS.TD_CTRL.REDUCE_VE is set, TDCS.FEATURE_PARAVIRT_CTLS is all-0 */ + + /* CPUID(0xb), Per SDM, CPUID(0x1F, *) is a preferred superset to leaf CPUID(0xB,*) */ + /* The cpuid value is different for different cpu topology. + * Test cases for cpu topology must be tested under specified vcpu configuration. + * Disable these cases by default, configure cpu topology in QEMU command before enable the cases here + */ + + /* -smp 16,sockets=1,dies=2,threads=2,cores=4, cpuid of cpu0 */ + /* Uncomment the following cases before testing */ + EXP_CPUID_BYTE(0xb, 0, eax, 0x1, VER1_5); + EXP_CPUID_BYTE(0xb, 0, ebx, 0x2, VER1_5); + EXP_CPUID_BYTE(0xb, 0, ecx, 0x0100, VER1_5); + EXP_CPUID_BYTE(0xb, 0, edx, 0, VER1_5); + EXP_CPUID_BYTE(0xb, 1, eax, 0x4, VER1_5); + EXP_CPUID_BYTE(0xb, 1, ebx, 0x10, VER1_5); + EXP_CPUID_BYTE(0xb, 1, ecx, 0x0201, VER1_5); + EXP_CPUID_BYTE(0xb, 1, edx, 0, VER1_5); + EXP_CPUID_BYTE(0xb, 2, eax, 0, VER1_5); + EXP_CPUID_BYTE(0xb, 2, ebx, 0, VER1_5); + EXP_CPUID_BYTE(0xb, 2, ecx, 0x2, VER1_5); + EXP_CPUID_BYTE(0xb, 2, edx, 0, VER1_5); + + /* CPUID(0x1f) -smp 16,sockets=1,dies=2,threads=2,cores=4, cpuid of cpu0 */ + /* Uncomment the following cases before testing */ + EXP_CPUID_BYTE(0x1f, 0, eax, 0x1, VER1_5); + EXP_CPUID_BYTE(0x1f, 0, ebx, 0x2, VER1_5); + EXP_CPUID_BYTE(0x1f, 0, ecx, 0x0100, VER1_5); + EXP_CPUID_BYTE(0x1f, 0, edx, 0, VER1_5); + EXP_CPUID_BYTE(0x1f, 1, eax, 0x3, VER1_5); + EXP_CPUID_BYTE(0x1f, 1, ebx, 0x8, VER1_5); + EXP_CPUID_BYTE(0x1f, 1, ecx, 0x0201, VER1_5); + EXP_CPUID_BYTE(0x1f, 1, edx, 0, VER1_5); + EXP_CPUID_BYTE(0x1f, 2, eax, 0x4, VER1_5); + EXP_CPUID_BYTE(0x1f, 2, ebx, 0x10, VER1_5); + EXP_CPUID_BYTE(0x1f, 2, ecx, 0x502, VER1_5); + EXP_CPUID_BYTE(0x1f, 2, edx, 0, VER1_5); + EXP_CPUID_BYTE(0x1f, 3, eax, 0, VER1_5); + EXP_CPUID_BYTE(0x1f, 3, ebx, 0, VER1_5); + EXP_CPUID_BYTE(0x1f, 3, ecx, 0x3, VER1_5); + EXP_CPUID_BYTE(0x1f, 3, edx, 0, VER1_5); + +} +#endif diff --git a/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_tp2.h b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_tp2.h new file mode 100644 index 00000000..b98050be --- /dev/null +++ b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_tp2.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include "tdx-compliance.h" + +#define DEF_CPUID_TEST(_leaf, _subleaf) \ +{ \ + .name = "CPUID_" #_leaf "_" #_subleaf, \ + .leaf = _leaf, \ + .subleaf = _subleaf, \ +} + +#define EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + int bnr = _bit_nr; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_nr "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = BIT(bnr); \ + t->regs._reg.expect = BIT(bnr) * (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BIT(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn) \ + EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, 0xbad, 0xbad) + +#define EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg; \ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = 0xffffffff; \ + t->regs._reg.expect = (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BYTE(_leaf, _subleaf, _reg, _val, _vsn) \ + EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, 0xbad, 0xbad) +#define EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, _td_ctl, _pv_ctl) do {\ + int i = 0; \ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_e ":" #_bit_s "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + for (i = _bit_s; i <= (_bit_e); i++) { \ + t->regs._reg.mask |= BIT(i); \ + } \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_RES_BITS(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn) \ + EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, 0xbad, 0xbad) + +#ifdef AUTOGEN_CPUID +void initial_cpuid(void); +#else +void initial_cpuid(void) +{ +/********* The following test cases are defined for #VE reduction. *********/ + +/* + * There are 3 configurations in total: + * 1. By default -- when #VE Reduction is enabled: TDCS.TD_CTRL.REDUCE_VE ==1 , + * TDCS.FEATURE_PARAVIRT_CTLS is all-0. + * 2. TD_CTLS.REDUCE_VE == 1, FEATURE_PARAVIRT_CTLS == 1 + * 3. Backward-Compatible -- when #VE reduction is not enabled: TD_CTLS.REDUCE_VE is 0. + */ + +/* 1. By default, TDCS.TD_CTRL.REDUCE_VE is set, TDCS.FEATURE_PARAVIRT_CTLS is all-0 */ + + /* CPUID(0xb), Per SDM, CPUID(0x1F, *) is a preferred superset to leaf CPUID(0xB,*) */ + /* The cpuid value is different for different cpu topology. + * Test cases for cpu topology must be tested under specified vcpu configuration. + * Disable these cases by default, configure cpu topology in QEMU command before enable the cases here + */ + + /* -smp 12,sockets=1,threads=3,cores=4, cpuid of cpu11 */ + /* Uncomment the following cases before testing */ + EXP_CPUID_BYTE(0xb, 0, eax, 0x2, VER1_5); + EXP_CPUID_BYTE(0xb, 0, ebx, 0x3, VER1_5); + EXP_CPUID_BYTE(0xb, 0, ecx, 0x0100, VER1_5); + EXP_CPUID_BYTE(0xb, 0, edx, 0xe, VER1_5); + EXP_CPUID_BYTE(0xb, 1, eax, 0x4, VER1_5); + EXP_CPUID_BYTE(0xb, 1, ebx, 0xc, VER1_5); + EXP_CPUID_BYTE(0xb, 1, ecx, 0x0201, VER1_5); + EXP_CPUID_BYTE(0xb, 1, edx, 0xe, VER1_5); + EXP_CPUID_BYTE(0xb, 2, eax, 0x0, VER1_5); + EXP_CPUID_BYTE(0xb, 2, ebx, 0x0, VER1_5); + EXP_CPUID_BYTE(0xb, 2, ecx, 0x2, VER1_5); + EXP_CPUID_BYTE(0xb, 2, edx, 0xe, VER1_5); + +} +#endif diff --git a/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_tp3.h b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_tp3.h new file mode 100644 index 00000000..9976776b --- /dev/null +++ b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_tp3.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include "tdx-compliance.h" + +#define DEF_CPUID_TEST(_leaf, _subleaf) \ +{ \ + .name = "CPUID_" #_leaf "_" #_subleaf, \ + .leaf = _leaf, \ + .subleaf = _subleaf, \ +} + +#define EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + int bnr = _bit_nr; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_nr "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = BIT(bnr); \ + t->regs._reg.expect = BIT(bnr) * (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BIT(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn) \ + EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, 0xbad, 0xbad) + +#define EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg; \ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = 0xffffffff; \ + t->regs._reg.expect = (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BYTE(_leaf, _subleaf, _reg, _val, _vsn) \ + EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, 0xbad, 0xbad) +#define EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, _td_ctl, _pv_ctl) do {\ + int i = 0; \ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_e ":" #_bit_s "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + for (i = _bit_s; i <= (_bit_e); i++) { \ + t->regs._reg.mask |= BIT(i); \ + } \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_RES_BITS(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn) \ + EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, 0xbad, 0xbad) + +#ifdef AUTOGEN_CPUID +void initial_cpuid(void); +#else +void initial_cpuid(void) +{ +/********* The following test cases are defined for #VE reduction. *********/ + +/* + * There are 3 configurations in total: + * 1. By default -- when #VE Reduction is enabled: TDCS.TD_CTRL.REDUCE_VE ==1 , + * TDCS.FEATURE_PARAVIRT_CTLS is all-0. + * 2. TD_CTLS.REDUCE_VE == 1, FEATURE_PARAVIRT_CTLS == 1 + * 3. Backward-Compatible -- when #VE reduction is not enabled: TD_CTLS.REDUCE_VE is 0. + */ + +/* 1. By default, TDCS.TD_CTRL.REDUCE_VE is set, TDCS.FEATURE_PARAVIRT_CTLS is all-0 */ + + /* CPUID(0x1f) -smp 48,sockets=2,dies=2,threads=3,cores=4, cpuid of cpu47 */ + /* Uncomment the following cases before testing */ + EXP_CPUID_BYTE(0x1f, 0, eax, 0x2, VER1_5); + EXP_CPUID_BYTE(0x1f, 0, ebx, 0x3, VER1_5); + EXP_CPUID_BYTE(0x1f, 0, ecx, 0x0100, VER1_5); + EXP_CPUID_BYTE(0x1f, 0, edx, 0x3e, VER1_5); + EXP_CPUID_BYTE(0x1f, 1, eax, 0x4, VER1_5); + EXP_CPUID_BYTE(0x1f, 1, ebx, 0xc, VER1_5); + EXP_CPUID_BYTE(0x1f, 1, ecx, 0x0201, VER1_5); + EXP_CPUID_BYTE(0x1f, 1, edx, 0x3e, VER1_5); + EXP_CPUID_BYTE(0x1f, 2, eax, 0x5, VER1_5); + EXP_CPUID_BYTE(0x1f, 2, ebx, 0x18, VER1_5); + EXP_CPUID_BYTE(0x1f, 2, ecx, 0x0502, VER1_5); + EXP_CPUID_BYTE(0x1f, 2, edx, 0x3e, VER1_5); + EXP_CPUID_BYTE(0x1f, 3, eax, 0, VER1_5); + EXP_CPUID_BYTE(0x1f, 3, ebx, 0, VER1_5); + EXP_CPUID_BYTE(0x1f, 3, ecx, 0x3, VER1_5); + EXP_CPUID_BYTE(0x1f, 3, edx, 0x3e, VER1_5); + +} +#endif diff --git a/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ve1.h b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ve1.h new file mode 100644 index 00000000..37310dbd --- /dev/null +++ b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ve1.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include "tdx-compliance.h" + +#define DEF_CPUID_TEST(_leaf, _subleaf) \ +{ \ + .name = "CPUID_" #_leaf "_" #_subleaf, \ + .leaf = _leaf, \ + .subleaf = _subleaf, \ +} + +#define EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + int bnr = _bit_nr; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_nr "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = BIT(bnr); \ + t->regs._reg.expect = BIT(bnr) * (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BIT(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn) \ + EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, 0xbad, 0xbad) + +#define EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg; \ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = 0xffffffff; \ + t->regs._reg.expect = (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BYTE(_leaf, _subleaf, _reg, _val, _vsn) \ + EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, 0xbad, 0xbad) +#define EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, _td_ctl, _pv_ctl) do {\ + int i = 0; \ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_e ":" #_bit_s "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + for (i = _bit_s; i <= (_bit_e); i++) { \ + t->regs._reg.mask |= BIT(i); \ + } \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_RES_BITS(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn) \ + EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, 0xbad, 0xbad) + +#ifdef AUTOGEN_CPUID +void initial_cpuid(void); +#else +void initial_cpuid(void) +{ +/********* The following test cases are defined for #VE reduction. *********/ + +/* + * There are 3 configurations in total: + * 1. By default -- when #VE Reduction is enabled: TDCS.TD_CTRL.REDUCE_VE ==1 , + * TDCS.FEATURE_PARAVIRT_CTLS is all-0. + * 2. TD_CTLS.REDUCE_VE == 1, FEATURE_PARAVIRT_CTLS == 1 + * 3. Backward-Compatible -- when #VE reduction is not enabled: TD_CTLS.REDUCE_VE is 0. + */ + +/* 3. Backward-Compatible -- when #VE reduction is not enabled: TD_CTLS.REDUCE_VE is 0. */ +// /* CPUID(0x2) */ + EXP_CPUID_BYTE_CTL(0x2, 0, eax, 0, VER1_5, 0, 0); //trigger #VE +} +#endif diff --git a/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ve2.h b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ve2.h new file mode 100644 index 00000000..4e8f1333 --- /dev/null +++ b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ve2.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include "tdx-compliance.h" + +#define DEF_CPUID_TEST(_leaf, _subleaf) \ +{ \ + .name = "CPUID_" #_leaf "_" #_subleaf, \ + .leaf = _leaf, \ + .subleaf = _subleaf, \ +} + +#define EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + int bnr = _bit_nr; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_nr "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = BIT(bnr); \ + t->regs._reg.expect = BIT(bnr) * (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BIT(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn) \ + EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, 0xbad, 0xbad) + +#define EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg; \ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = 0xffffffff; \ + t->regs._reg.expect = (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BYTE(_leaf, _subleaf, _reg, _val, _vsn) \ + EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, 0xbad, 0xbad) +#define EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, _td_ctl, _pv_ctl) do {\ + int i = 0; \ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_e ":" #_bit_s "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + for (i = _bit_s; i <= (_bit_e); i++) { \ + t->regs._reg.mask |= BIT(i); \ + } \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_RES_BITS(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn) \ + EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, 0xbad, 0xbad) + +#ifdef AUTOGEN_CPUID +void initial_cpuid(void); +#else +void initial_cpuid(void) +{ +/********* The following test cases are defined for #VE reduction. *********/ + +/* + * There are 3 configurations in total: + * 1. By default -- when #VE Reduction is enabled: TDCS.TD_CTRL.REDUCE_VE ==1 , + * TDCS.FEATURE_PARAVIRT_CTLS is all-0. + * 2. TD_CTLS.REDUCE_VE == 1, FEATURE_PARAVIRT_CTLS == 1 + * 3. Backward-Compatible -- when #VE reduction is not enabled: TD_CTLS.REDUCE_VE is 0. + */ + +/* 3. Backward-Compatible -- when #VE reduction is not enabled: TD_CTLS.REDUCE_VE is 0. */ +// /* CPUID(0x6) */ + EXP_CPUID_BIT_CTL(0x6, 0, eax, 2, 0x0, VER1_5, 0, 0);//trigger #VE +} +#endif diff --git a/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ve3.h b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ve3.h new file mode 100644 index 00000000..10952c47 --- /dev/null +++ b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ve3.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include "tdx-compliance.h" + +#define DEF_CPUID_TEST(_leaf, _subleaf) \ +{ \ + .name = "CPUID_" #_leaf "_" #_subleaf, \ + .leaf = _leaf, \ + .subleaf = _subleaf, \ +} + +#define EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + int bnr = _bit_nr; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_nr "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = BIT(bnr); \ + t->regs._reg.expect = BIT(bnr) * (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BIT(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn) \ + EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, 0xbad, 0xbad) + +#define EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg; \ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = 0xffffffff; \ + t->regs._reg.expect = (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BYTE(_leaf, _subleaf, _reg, _val, _vsn) \ + EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, 0xbad, 0xbad) +#define EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, _td_ctl, _pv_ctl) do {\ + int i = 0; \ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_e ":" #_bit_s "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + for (i = _bit_s; i <= (_bit_e); i++) { \ + t->regs._reg.mask |= BIT(i); \ + } \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_RES_BITS(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn) \ + EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, 0xbad, 0xbad) + +#ifdef AUTOGEN_CPUID +void initial_cpuid(void); +#else +void initial_cpuid(void) +{ +/********* The following test cases are defined for #VE reduction. *********/ + +/* + * There are 3 configurations in total: + * 1. By default -- when #VE Reduction is enabled: TDCS.TD_CTRL.REDUCE_VE ==1 , + * TDCS.FEATURE_PARAVIRT_CTLS is all-0. + * 2. TD_CTLS.REDUCE_VE == 1, FEATURE_PARAVIRT_CTLS == 1 + * 3. Backward-Compatible -- when #VE reduction is not enabled: TD_CTLS.REDUCE_VE is 0. + */ + +/* 3. Backward-Compatible -- when #VE reduction is not enabled: TD_CTLS.REDUCE_VE is 0. */ +// /* CPUID(0x9), enumerated by virtual CPUID(1).ECX[18] */ +// /* DCA(dca) */ +// EXP_CPUID_BIT_CTL(0x1, 0, ecx, 18, 0x0, VER1_5, 0, 0); //not supported by VMM + EXP_CPUID_BYTE_CTL(0x9, 0, eax, 0x0, VER1_5, 0, 0); //trigger #VE +} +#endif diff --git a/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ve4.h b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ve4.h new file mode 100644 index 00000000..15d8d7ae --- /dev/null +++ b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ve4.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include "tdx-compliance.h" + +#define DEF_CPUID_TEST(_leaf, _subleaf) \ +{ \ + .name = "CPUID_" #_leaf "_" #_subleaf, \ + .leaf = _leaf, \ + .subleaf = _subleaf, \ +} + +#define EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + int bnr = _bit_nr; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_nr "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = BIT(bnr); \ + t->regs._reg.expect = BIT(bnr) * (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BIT(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn) \ + EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, 0xbad, 0xbad) + +#define EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg; \ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = 0xffffffff; \ + t->regs._reg.expect = (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BYTE(_leaf, _subleaf, _reg, _val, _vsn) \ + EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, 0xbad, 0xbad) +#define EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, _td_ctl, _pv_ctl) do {\ + int i = 0; \ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_e ":" #_bit_s "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + for (i = _bit_s; i <= (_bit_e); i++) { \ + t->regs._reg.mask |= BIT(i); \ + } \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_RES_BITS(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn) \ + EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, 0xbad, 0xbad) + +#ifdef AUTOGEN_CPUID +void initial_cpuid(void); +#else +void initial_cpuid(void) +{ +/********* The following test cases are defined for #VE reduction. *********/ + +/* + * There are 3 configurations in total: + * 1. By default -- when #VE Reduction is enabled: TDCS.TD_CTRL.REDUCE_VE ==1 , + * TDCS.FEATURE_PARAVIRT_CTLS is all-0. + * 2. TD_CTLS.REDUCE_VE == 1, FEATURE_PARAVIRT_CTLS == 1 + * 3. Backward-Compatible -- when #VE reduction is not enabled: TD_CTLS.REDUCE_VE is 0. + */ + +/* 3. Backward-Compatible -- when #VE reduction is not enabled: TD_CTLS.REDUCE_VE is 0. */ +// /* CPUID(0xb), Per SDM, CPUID(0x1F, *) is a preferred superset to leaf CPUID(0xB,*) */ + EXP_CPUID_BYTE_CTL(0xb, 0, eax, 0, VER1_5, 0, 0); //trigger #VE +} +#endif diff --git a/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ve5.h b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ve5.h new file mode 100644 index 00000000..e63f567f --- /dev/null +++ b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ve5.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include "tdx-compliance.h" + +#define DEF_CPUID_TEST(_leaf, _subleaf) \ +{ \ + .name = "CPUID_" #_leaf "_" #_subleaf, \ + .leaf = _leaf, \ + .subleaf = _subleaf, \ +} + +#define EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + int bnr = _bit_nr; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_nr "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = BIT(bnr); \ + t->regs._reg.expect = BIT(bnr) * (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BIT(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn) \ + EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, 0xbad, 0xbad) + +#define EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg; \ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = 0xffffffff; \ + t->regs._reg.expect = (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BYTE(_leaf, _subleaf, _reg, _val, _vsn) \ + EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, 0xbad, 0xbad) +#define EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, _td_ctl, _pv_ctl) do {\ + int i = 0; \ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_e ":" #_bit_s "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + for (i = _bit_s; i <= (_bit_e); i++) { \ + t->regs._reg.mask |= BIT(i); \ + } \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_RES_BITS(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn) \ + EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, 0xbad, 0xbad) + +#ifdef AUTOGEN_CPUID +void initial_cpuid(void); +#else +void initial_cpuid(void) +{ +/********* The following test cases are defined for #VE reduction. *********/ + +/* + * There are 3 configurations in total: + * 1. By default -- when #VE Reduction is enabled: TDCS.TD_CTRL.REDUCE_VE ==1 , + * TDCS.FEATURE_PARAVIRT_CTLS is all-0. + * 2. TD_CTLS.REDUCE_VE == 1, FEATURE_PARAVIRT_CTLS == 1 + * 3. Backward-Compatible -- when #VE reduction is not enabled: TD_CTLS.REDUCE_VE is 0. + */ + +/* 3. Backward-Compatible -- when #VE reduction is not enabled: TD_CTLS.REDUCE_VE is 0. */ +// /* CPUID(0xc) Reserved */ + EXP_CPUID_BYTE_CTL(0xc, 0, eax, 0, VER1_5, 0, 0); //trigger #VE +} +#endif diff --git a/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ve6.h b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ve6.h new file mode 100644 index 00000000..c8027722 --- /dev/null +++ b/KVM/qemu/deps/tdx_ve_reduction/tdx_cpuid_ve6.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include "tdx-compliance.h" + +#define DEF_CPUID_TEST(_leaf, _subleaf) \ +{ \ + .name = "CPUID_" #_leaf "_" #_subleaf, \ + .leaf = _leaf, \ + .subleaf = _subleaf, \ +} + +#define EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + int bnr = _bit_nr; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_nr "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = BIT(bnr); \ + t->regs._reg.expect = BIT(bnr) * (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BIT(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn) \ + EXP_CPUID_BIT_CTL(_leaf, _subleaf, _reg, _bit_nr, _val, _vsn, 0xbad, 0xbad) + +#define EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, _td_ctl, _pv_ctl) do {\ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg; \ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + t->regs._reg.mask = 0xffffffff; \ + t->regs._reg.expect = (_val); \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_BYTE(_leaf, _subleaf, _reg, _val, _vsn) \ + EXP_CPUID_BYTE_CTL(_leaf, _subleaf, _reg, _val, _vsn, 0xbad, 0xbad) +#define EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, _td_ctl, _pv_ctl) do {\ + int i = 0; \ + struct test_cpuid *t; \ + t = kzalloc_obj(struct test_cpuid, GFP_KERNEL); \ + t->name = "CPUID(" #_leaf "," #_subleaf ")." #_reg "[" #_bit_e ":" #_bit_s "]";\ + t->version = (_vsn); \ + t->leaf = (_leaf); \ + t->subleaf = (_subleaf); \ + for (i = _bit_s; i <= (_bit_e); i++) { \ + t->regs._reg.mask |= BIT(i); \ + } \ + t->tdcs_td_ctl = (_td_ctl); \ + t->tdcs_feature_pv_ctl = (_pv_ctl); \ + list_add_tail(&t->list, &cpuid_list); \ +} while (0) + +#define EXP_CPUID_RES_BITS(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn) \ + EXP_CPUID_RES_BITS_CTL(_leaf, _subleaf, _reg, _bit_s, _bit_e, _vsn, 0xbad, 0xbad) + +#ifdef AUTOGEN_CPUID +void initial_cpuid(void); +#else +void initial_cpuid(void) +{ +/********* The following test cases are defined for #VE reduction. *********/ + +/* + * There are 3 configurations in total: + * 1. By default -- when #VE Reduction is enabled: TDCS.TD_CTRL.REDUCE_VE ==1 , + * TDCS.FEATURE_PARAVIRT_CTLS is all-0. + * 2. TD_CTLS.REDUCE_VE == 1, FEATURE_PARAVIRT_CTLS == 1 + * 3. Backward-Compatible -- when #VE reduction is not enabled: TD_CTLS.REDUCE_VE is 0. + */ + +/* 3. Backward-Compatible -- when #VE reduction is not enabled: TD_CTLS.REDUCE_VE is 0. */ +// /* CPUID(0x1f) */ + EXP_CPUID_BYTE_CTL(0x1f, 0, eax, 0, VER1_5, 0, 0); //trigger #VE +} +#endif diff --git a/KVM/qemu/deps/xsaves_workload/security.c b/KVM/qemu/deps/xsaves_workload/security.c new file mode 100644 index 00000000..1f53134d --- /dev/null +++ b/KVM/qemu/deps/xsaves_workload/security.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2026 Intel Corporation +/* + * XSAVES security test payload. + * + * Installs a SIGHUP handler that overwrites the fpstate pointer in the + * signal frame with an invalid address, then raises SIGHUP against itself. + * On return from the handler the kernel must detect the bogus fpstate and + * terminate the process; a well-behaved kernel additionally emits a + * diagnostic in dmesg. Silent completion is treated as a regression by the + * calling test harness. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +void sighup(int sig, siginfo_t *info, void *ctxt) +{ + ucontext_t *uctxt = ctxt; + struct sigcontext *sctxt = (void *)&uctxt->uc_mcontext; + + printf("SIGHUP! %p\n", sctxt->fpstate); + sctxt->fpstate = (void *)1; +} + +int main(void) +{ + struct sigaction sa = { + .sa_sigaction = sighup, + .sa_flags = SA_SIGINFO, + }; + + sigaction(SIGHUP, &sa, NULL); + kill(getpid(), SIGHUP); + + return 0; +} diff --git a/KVM/qemu/deps/xsaves_workload/vz_novz_performance.c b/KVM/qemu/deps/xsaves_workload/vz_novz_performance.c new file mode 100644 index 00000000..db3d5b3a --- /dev/null +++ b/KVM/qemu/deps/xsaves_workload/vz_novz_performance.c @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2026 Intel Corporation +/* + * XSAVES workload payload: tight SSE loop with an optional leading + * vzeroupper. Build twice from the same source, once with -DVZ and once + * without, so the test harness can compare wall-clock time between the + * two variants under KVM. + */ + +int main(void) +{ + unsigned long long i; + +#ifdef VZ + asm("vzeroupper"); +#endif + for (i = 0; i < 0xc0000000; i++) { + asm("movups (%rsp), %xmm2"); + asm("addps %xmm1, %xmm2"); + } + return 0; +} diff --git a/KVM/qemu/ept_5lp_basic.cfg b/KVM/qemu/ept_5lp_basic.cfg index 0189b1bc..da1a91d7 100644 --- a/KVM/qemu/ept_5lp_basic.cfg +++ b/KVM/qemu/ept_5lp_basic.cfg @@ -2,12 +2,10 @@ type = ept_5lp_basic virt_test_type = qemu vm_accelerator = kvm - # Don't create/remove guest images - force_create_image = no - remove_image = no start_vm = no # Stop VM after testing kill_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. shell_prompt = "^\[.*\][\#\$]\s*$" vga = std auto_cpu_model = "no" diff --git a/KVM/qemu/feature_test.cfg b/KVM/qemu/feature_test.cfg index 9bd358fe..39b14ed7 100644 --- a/KVM/qemu/feature_test.cfg +++ b/KVM/qemu/feature_test.cfg @@ -1,6 +1,8 @@ - feature_test: type = feature_test kill_vm_on_error = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" start_vm = yes vga = std pip_packages = python3 python3-pip @@ -45,6 +47,98 @@ - nmis: feature_dir_names = "nmis" no tdvm + - adaptive_pebs: + no tdvm + feature_dir_names = "pmu" + disable_parallel_run = yes + pre_command = 'echo 0 > /proc/sys/kernel/nmi_watchdog && modprobe -r kvm_intel && modprobe kvm_intel enable_mediated_pmu=Y' + post_command = 'echo 1 > /proc/sys/kernel/nmi_watchdog && modprobe -r kvm_intel && modprobe kvm_intel' + test_scen = "tests-adaptive_pebs" + - topology_die_cluster_1: + no tdvm + feature_dir_names = "topology" + test_scen = "tests-die_cluster_1" + mem = 8192 + smp = 192 + vcpu_maxcpus = 192 + vcpu_sockets = 1 + vcpu_dies = 4 + vcpu_cores = 48 + vcpu_threads = 1 + machine_type_extra_params = "smp-cache.0.cache=l2,smp-cache.0.topology=module,kernel-irqchip=split" + extra_params = "-smp cpus=192,sockets=1,dies=4,modules=24,cores=2,threads=1" + login_timeout = 600 + cmd_timeout = 300 + - topology_die_cluster_2: + no tdvm + feature_dir_names = "topology" + test_scen = "tests-die_cluster_2" + mem = 8192 + smp = 192 + vcpu_maxcpus = 192 + vcpu_sockets = 2 + vcpu_dies = 2 + vcpu_cores = 48 + vcpu_threads = 1 + machine_type_extra_params = "smp-cache.0.cache=l2,smp-cache.0.topology=module,kernel-irqchip=split" + extra_params = "-smp cpus=192,sockets=2,dies=2,modules=24,cores=2,threads=1" + login_timeout = 600 + cmd_timeout = 300 + - topology_cores_per_socket: + no tdvm + feature_dir_names = "topology" + test_scen = "tests-cores_per_socket" + mem = 8192 + smp = 192 + vcpu_maxcpus = 192 + vcpu_sockets = 2 + vcpu_dies = 2 + vcpu_cores = 48 + vcpu_threads = 1 + machine_type_extra_params = "smp-cache.0.cache=l2,smp-cache.0.topology=module,kernel-irqchip=split" + extra_params = "-smp cpus=192,sockets=2,dies=2,modules=24,cores=2,threads=1" + login_timeout = 600 + cmd_timeout = 300 + - topology_sockets_number: + no tdvm + feature_dir_names = "topology" + test_scen = "tests-sockets_number" + mem = 8192 + smp = 192 + vcpu_maxcpus = 192 + vcpu_sockets = 2 + vcpu_dies = 2 + vcpu_cores = 48 + vcpu_threads = 1 + backend_mem = memory-backend-ram + use_mem = no + mem_devs = "mem0 mem1" + size_mem0 = 4096M + size_mem1 = 4096M + guest_numa_nodes = "node0 node1" + numa_memdev_node0 = mem-mem0 + numa_memdev_node1 = mem-mem1 + numa_cpus_node0 = "0-95" + numa_cpus_node1 = "96-191" + machine_type_extra_params = "smp-cache.0.cache=l2,smp-cache.0.topology=module,kernel-irqchip=split" + extra_params = "-smp cpus=192,sockets=2,dies=2,modules=24,cores=2,threads=1" + login_timeout = 600 + cmd_timeout = 300 + - topology_cache_topo: + no tdvm + feature_dir_names = "topology" + test_scen = "tests-cache_topo" + mem = 8192 + smp = 192 + vcpu_maxcpus = 192 + vcpu_sockets = 1 + vcpu_dies = 4 + vcpu_cores = 48 + vcpu_threads = 1 + machine_type_extra_params = "smp-cache.0.cache=l2,smp-cache.0.topology=module,kernel-irqchip=split" + extra_params = "-smp cpus=192,sockets=1,dies=4,modules=24,cores=2,threads=1" + login_timeout = 600 + cmd_timeout = 300 variants: - vm: - tdvm: diff --git a/KVM/qemu/host_cpu_offline_online.cfg b/KVM/qemu/host_cpu_offline_online.cfg index 6550c6ac..ed8cfefe 100644 --- a/KVM/qemu/host_cpu_offline_online.cfg +++ b/KVM/qemu/host_cpu_offline_online.cfg @@ -2,13 +2,12 @@ type = host_cpu_offline_online virt_test_type = qemu vm_accelerator = kvm - # Don't create/remove guest images - force_create_image = no - remove_image = no # Automatically start VM start_vm = yes # Stop VM after testing kill_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" variants: - vm: - tdvm: diff --git a/KVM/qemu/kvm_memory_hotplug.cfg b/KVM/qemu/kvm_memory_hotplug.cfg new file mode 100644 index 00000000..07a3d162 --- /dev/null +++ b/KVM/qemu/kvm_memory_hotplug.cfg @@ -0,0 +1,16 @@ +- kvm_memory_hotplug: + type = kvm_memory_hotplug + virt_test_type = qemu + vm_accelerator = kvm + start_vm = no + # Stop VM after testing + kill_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" + vga = std + target_mems = "plug1" + slots_mem = 1 + maxmem_mem = 8G + variants: + - 1G: + size_mem_plug1 = 1G diff --git a/KVM/qemu/live_migration.cfg b/KVM/qemu/live_migration.cfg new file mode 100644 index 00000000..100ee77f --- /dev/null +++ b/KVM/qemu/live_migration.cfg @@ -0,0 +1,116 @@ +- live_migration: + virt_test_type = qemu + vm_accelerator = kvm + start_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" + image_snapshot = no + vga = std + auto_cpu_model = "no" + cpu_model = host + migration_test_command = help + migration_bg_command = cd /tmp && nohup ping localhost > /dev/null 2>&1 & + migration_bg_check_command = pgrep ping + migration_bg_kill_command = pkill -9 ping + login_timeout = 360 + mig_timeout = 300 + migration_protocol = tcp + variants: + - vm_1vcpu_lm: + type = live_migration_repeat + smp = 1 + vcpu_maxcpus = 1 + migration_iterations = 1 + iteration_sleep_time = 0 + - vm_2vcpu_lm: + type = live_migration_repeat + smp = 2 + vcpu_maxcpus = 2 + migration_iterations = 1 + iteration_sleep_time = 0 + - vm_4vcpu_lm: + type = live_migration_repeat + smp = 4 + vcpu_maxcpus = 4 + migration_iterations = 1 + iteration_sleep_time = 0 + - vm_8vcpu_lm: + type = live_migration_repeat + smp = 8 + vcpu_maxcpus = 8 + migration_iterations = 1 + iteration_sleep_time = 0 + - vm_lm_2times: + type = live_migration_repeat + smp = 4 + vcpu_maxcpus = 4 + migration_iterations = 2 + iteration_sleep_time = 0 + - vm_stress_local_lm: + type = live_migration_repeat + smp = 4 + vcpu_maxcpus = 4 + migration_iterations = 10 + iteration_sleep_time = 5 + - cpu_flag_after_lm: + type = live_migration_verify + smp = 4 + vcpu_maxcpus = 4 + verify_target = cpu_flags + - cpuid_after_lm: + type = live_migration_verify + smp = 4 + vcpu_maxcpus = 4 + verify_target = cpuid + cpuid_dump_cmd = cpuid -1 -r + - cpu_num_after_lm: + type = live_migration_verify + smp = 4 + vcpu_maxcpus = 4 + verify_target = cpu_num + - mem_size_after_lm: + type = live_migration_verify + smp = 4 + vcpu_maxcpus = 4 + verify_target = mem_size + - time_test_after_lm: + type = live_migration_verify + smp = 4 + vcpu_maxcpus = 4 + verify_target = time + drift_threshold = 10 + - vm_lm_continuity: + type = live_migration_verify + smp = 4 + vcpu_maxcpus = 4 + verify_target = continuity + continuity_cmd = cat /dev/urandom > /dev/null + continuity_check_cmd = pgrep -f "cat /dev/urandom" + - srf_lm: + type = live_migration_repeat + cpu_model = SierraForest-v6 + smp = 4 + vcpu_maxcpus = 4 + migration_iterations = 1 + iteration_sleep_time = 0 + - gnr_lm: + type = live_migration_repeat + cpu_model = GraniteRapids-v7 + smp = 4 + vcpu_maxcpus = 4 + migration_iterations = 1 + iteration_sleep_time = 0 + - spr_lm: + type = live_migration_repeat + cpu_model = SapphireRapids-v8 + smp = 4 + vcpu_maxcpus = 4 + migration_iterations = 1 + iteration_sleep_time = 0 + - cascadelake_lm: + type = live_migration_repeat + cpu_model = Cascadelake-Server-v7 + smp = 4 + vcpu_maxcpus = 4 + migration_iterations = 1 + iteration_sleep_time = 0 diff --git a/KVM/qemu/mce_einj.cfg b/KVM/qemu/mce_einj.cfg new file mode 100644 index 00000000..396d59c9 --- /dev/null +++ b/KVM/qemu/mce_einj.cfg @@ -0,0 +1,25 @@ +- mce_einj: + no Windows + type = mce_einj + virt_test_type = qemu + vm_accelerator = kvm + start_vm = yes + kill_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" + auto_cpu_model = "no" + cpu_model = host + test_dir = '/tmp' + source_file = 'victim.c' + exec_file = 'victim' + variants: + - srao_lmce_off: + error_type = "0x10" + - srao_lmce_on: + error_type = "0x10" + cpu_model_flags = ",lmce=on" + lmce_state = "on" + - srar_lmce_on: + error_type = "0x10" + cpu_model_flags = ",lmce=on" + lmce_state = "on" diff --git a/KVM/qemu/mce_hmp.cfg b/KVM/qemu/mce_hmp.cfg new file mode 100644 index 00000000..c7bf6732 --- /dev/null +++ b/KVM/qemu/mce_hmp.cfg @@ -0,0 +1,38 @@ +- mce_hmp: + type = mce_hmp + virt_test_type = qemu + vm_accelerator = kvm + start_vm = no + kill_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" + vga = std + auto_cpu_model = "no" + cpu_model = host + smp = 4 + login_timeout = 240 + # Source used by SRAR to allocate a target guest page. + test_dir = "/tmp" + source_file = "victim.c" + exec_file = "victim" + variants: + - sanity_lmce_off: + lmce_state = off + lmce_action = check + cpu_model_flags = ",lmce=off" + - sanity_lmce_on: + lmce_state = on + lmce_action = check + cpu_model_flags = ",lmce=on" + - srao_lmce_off: + lmce_state = off + lmce_action = srao + cpu_model_flags = ",lmce=off" + - srao_lmce_on: + lmce_state = on + lmce_action = srao + cpu_model_flags = ",lmce=on" + - srar_lmce_on: + lmce_state = on + lmce_action = srar + cpu_model_flags = ",lmce=on" diff --git a/KVM/qemu/multi_vms.cfg b/KVM/qemu/multi_vms.cfg index 3c398080..8667ce0f 100644 --- a/KVM/qemu/multi_vms.cfg +++ b/KVM/qemu/multi_vms.cfg @@ -5,6 +5,8 @@ vms = "vm1 vm2" image_snapshot = yes start_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" vga = std auto_cpu_model = "no" cpu_model = host diff --git a/KVM/qemu/nested_boot.cfg b/KVM/qemu/nested_boot.cfg new file mode 100644 index 00000000..2489725e --- /dev/null +++ b/KVM/qemu/nested_boot.cfg @@ -0,0 +1,45 @@ +- nested_boot: + type = nested_boot + virt_test_type = qemu + vm_accelerator = kvm + start_vm = no + kill_vm = yes + shell_prompt = "^\[.*\][\#\$]\s*$" + # Enable nested KVM on the host before booting L1. Runs in + # env_process.preprocess (host shell). If nested is already on, + # leave it untouched; otherwise reload kvm_intel with nested=1 and + # drop a marker so post_command can restore it. Fails the test + # (test error) if nested cannot be enabled. + pre_command = "if grep -qxE 'Y|1' /sys/module/kvm_intel/parameters/nested; then rm -f /tmp/.nested_kvm_toggled_by_case; else modprobe -r kvm_intel 2>/dev/null; modprobe kvm_intel nested=1; touch /tmp/.nested_kvm_toggled_by_case; fi; grep -qxE 'Y|1' /sys/module/kvm_intel/parameters/nested" + # Restore the host after the case: only disable nested again if this + # case was the one that enabled it (marker present). + post_command = "if [ -f /tmp/.nested_kvm_toggled_by_case ]; then modprobe -r kvm_intel 2>/dev/null; modprobe kvm_intel nested=0 2>/dev/null; rm -f /tmp/.nested_kvm_toggled_by_case; fi; true" + vga = std + smp = 16 + mem = 16384 + cpu_model_flags += ",+vmx" + l2_mem = 2048 + l2_smp = 2 + l2_ssh_port = 2222 + l2_boot_timeout = 180 + # L2 image is a raw reflink clone of L1, attached to L1 as image2 + # (virtio-blk) and consumed inside L1 as /dev/vdb by nested QEMU. + l2_image_basename = "l2_guest" + variants: + - linux_l2: + - nested_Cascadelake_Server: + cpu_model = "Cascadelake-Server-v7" + - nested_Cooperlake: + cpu_model = "Cooperlake-v2" + - nested_Denverton: + cpu_model = "Denverton-v3" + - nested_GraniteRapids: + cpu_model = "GraniteRapids-v7" + - nested_Icelake_Server: + cpu_model = "Icelake-Server-v9" + - nested_SapphireRapids: + cpu_model = "SapphireRapids-v8" + - nested_SierraForest: + cpu_model = "SierraForest-v6" + - nested_Snowridge: + cpu_model = "Snowridge-v4" diff --git a/KVM/qemu/nested_l2_test.cfg b/KVM/qemu/nested_l2_test.cfg new file mode 100644 index 00000000..62807a7a --- /dev/null +++ b/KVM/qemu/nested_l2_test.cfg @@ -0,0 +1,37 @@ +- nested_l2_test: + type = nested_l2_test + virt_test_type = qemu + vm_accelerator = kvm + start_vm = no + kill_vm = yes + shell_prompt = "^\[.*\][\#\$]\s*$" + # Enable nested KVM on the host before booting L1. Runs in + # env_process.preprocess (host shell). If nested is already on, + # leave it untouched; otherwise reload kvm_intel with nested=1 and + # drop a marker so post_command can restore it. Fails the test + # (test error) if nested cannot be enabled. + pre_command = "if grep -qxE 'Y|1' /sys/module/kvm_intel/parameters/nested; then rm -f /tmp/.nested_kvm_toggled_by_case; else modprobe -r kvm_intel 2>/dev/null; modprobe kvm_intel nested=1; touch /tmp/.nested_kvm_toggled_by_case; fi; grep -qxE 'Y|1' /sys/module/kvm_intel/parameters/nested" + # Restore the host after the case: only disable nested again if this + # case was the one that enabled it (marker present). + post_command = "if [ -f /tmp/.nested_kvm_toggled_by_case ]; then modprobe -r kvm_intel 2>/dev/null; modprobe kvm_intel nested=0 2>/dev/null; rm -f /tmp/.nested_kvm_toggled_by_case; fi; true" + vga = std + smp = 8 + mem = 8192 + cpu_model_flags += ",+vmx" + # L2 QEMU (nested) inside L1 + l2_mem = 2048 + l2_smp = 2 + l2_ssh_port = 2222 + l2_boot_timeout = 180 + # L2 image is a raw copy of L1 image, placed in the same host + # directory as L1. It is attached to L1 as image2 (virtio-blk), + # appearing inside L1 as /dev/vdb, and used directly by nested QEMU. + l2_image_basename = "l2_guest" + variants: + - pku: + l2_test_type = "pku" + cpu_model_flags += ",+pku" + - vpmu: + l2_test_type = "vpmu" + cpu_model_flags += ",pmu=on" + l2_cpu_flags = "pmu=on" diff --git a/KVM/qemu/nmis_nonmis.cfg b/KVM/qemu/nmis_nonmis.cfg new file mode 100644 index 00000000..815a7ac8 --- /dev/null +++ b/KVM/qemu/nmis_nonmis.cfg @@ -0,0 +1,14 @@ +- nmis_nonmis: + type = nmis_nonmis + kill_vm_on_error = yes + start_vm = no + auto_cpu_model = "no" + cpu_model = host + cpu_model_flags = ",-nmi-source" + shell_prompt = "^\[.*\][\#\$]\s*$" + vga = std + test_dir = '/home' + source_file = cpuid_check.c + exec_file = cpuid_check + fred_cpuid = 7 0 1 0 a 17 + cpuid = 7 0 1 0 a 20 diff --git a/KVM/qemu/pf_intercepts_ept.cfg b/KVM/qemu/pf_intercepts_ept.cfg new file mode 100644 index 00000000..84d8f4c9 --- /dev/null +++ b/KVM/qemu/pf_intercepts_ept.cfg @@ -0,0 +1,22 @@ +- pf_intercepts_ept: + type = pf_intercepts_ept + only Linux + virt_test_type = qemu + vm_accelerator = kvm + # Handler boots the guest itself so it can install the host tracepoint + # before the VM starts running. + start_vm = no + kill_vm = yes + # Default shell prompt for the centos image; override in runtime cfg for + # other guest OS images. + shell_prompt = "^\[.*\][\#\$]\s*$" + auto_cpu_model = "no" + cpu_model = host + vga = std + login_timeout = 240 + # Host EPT-enabled sysfs knob (Intel VMX). + ept_sysfs = /sys/module/kvm_intel/parameters/ept + # Time window (seconds) to observe guest kvm_exit events after boot. + trace_duration = 10 + # Intel VMX intr_info low 8 bits carry the vector; page fault is vector 14. + pf_vector = 14 diff --git a/KVM/qemu/provider/kvm_trace_utils.py b/KVM/qemu/provider/kvm_trace_utils.py new file mode 100644 index 00000000..303ddc99 --- /dev/null +++ b/KVM/qemu/provider/kvm_trace_utils.py @@ -0,0 +1,65 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +"""Shared helpers for the host KVM ``kvm/kvm_exit`` ftrace tracepoint. + +Used by tests that need to observe raw VMX exits from KVM (e.g. +``buslock_ve``, ``pf_intercepts_ept``). Enabling / disabling the same +tracepoint pattern is duplicated across those tests, so it lives here. +""" + +import os + +from avocado.utils import process + + +HOST_TRACE_DIR = "/sys/kernel/debug/tracing" +HOST_KVM_EXIT_ENABLE = os.path.join(HOST_TRACE_DIR, "events/kvm/kvm_exit/enable") +HOST_KVM_EXIT_FILTER = os.path.join(HOST_TRACE_DIR, "events/kvm/kvm_exit/filter") +HOST_TRACE_FILE = os.path.join(HOST_TRACE_DIR, "trace") + + +def enable_kvm_exit_trace(test, filter_expr=None): + """Enable the host ``kvm/kvm_exit`` tracepoint and clear the ring buffer. + + :param test: avocado test object; used to raise ``test.error`` if the + tracepoint cannot be enabled after write-back verification. + :param filter_expr: optional ftrace filter expression written to the + tracepoint's ``filter`` file before enabling. For example + ``"exit_reason == 0"`` restricts recorded exits to EXCEPTION_NMI + so the ring buffer stays tiny during a full guest boot. ``None`` + leaves the existing filter untouched. + """ + if filter_expr is not None: + process.run( + "echo '%s' > %s" % (filter_expr, HOST_KVM_EXIT_FILTER), + shell=True, + ) + process.run("echo 1 > %s" % HOST_KVM_EXIT_ENABLE, shell=True) + process.run("echo > %s" % HOST_TRACE_FILE, shell=True) + enabled = process.run( + "cat %s" % HOST_KVM_EXIT_ENABLE, shell=True + ).stdout_text.strip() + if enabled != "1": + test.error("kvm_exit tracepoint could not be enabled on host") + + +def disable_kvm_exit_trace(clear_filter=False): + """Best-effort disable of the ``kvm/kvm_exit`` tracepoint. + + :param clear_filter: when True, also reset the ftrace filter with + ``echo 0 > filter`` so subsequent runs start from a clean state. + Callers that set a filter in :func:`enable_kvm_exit_trace` should + pass ``True`` here to avoid leaking the filter into other tests. + """ + process.run( + "echo 0 > %s" % HOST_KVM_EXIT_ENABLE, + shell=True, ignore_status=True, + ) + if clear_filter: + process.run( + "echo 0 > %s" % HOST_KVM_EXIT_FILTER, + shell=True, ignore_status=True, + ) diff --git a/KVM/qemu/provider/lmce_utils.py b/KVM/qemu/provider/lmce_utils.py new file mode 100644 index 00000000..cfc2a57b --- /dev/null +++ b/KVM/qemu/provider/lmce_utils.py @@ -0,0 +1,172 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +# Author: Farrah Chen +# +# History: Jul. 2026 - Farrah Chen - creation + +"""Helpers shared by Local Machine Check Exception (LMCE) related tests. + +Provides the IA32 MSR / bit-mask / MCi_STATUS / MCG_STATUS constants used +to probe guest LMCE state and to shape MCE injections, plus the small +helpers reused by both the HMP-based (lmce.py) and EINJ-based (ras.py) +test flows. +""" + +import os + +from virttest import data_dir as virttest_data_dir +from virttest import utils_package + + +# IA32 MSR addresses used to probe LMCE support and configuration. +IA32_MCG_CAP = 0x179 +IA32_FEATURE_CONTROL = 0x3A +IA32_MCG_EXT_CTL = 0x4D0 + +# Enabling bits: all three must be set for guest to have LMCE fully enabled. +MCG_LMCE_P_MASK = 1 << 27 # IA32_MCG_CAP.LMCE_P +FEATURE_LMCE_ON_MASK = 1 << 20 # IA32_FEATURE_CONTROL.LMCE_ON +MCG_EXT_CTL_LMCE_EN_MASK = 1 # IA32_MCG_EXT_CTL.LMCE_EN + +# Precomputed IA32_MCi_STATUS values per Intel SDM Vol.3B Ch.15. +# SRAO = VAL(63) | UC(61) | EN(60) | MISCV(59) | ADDRV(58) | S(56) | mcacod=0x080 +# SRAR = SRAO | AR(55), with mcacod=0x134 (data-load uncorrected read). +MCI_STATUS_SRAO = 0xBD00000000000080 +MCI_STATUS_SRAR = 0xBD80000000000134 + +# MCG_STATUS values per Intel SDM Vol.3B Table 15-20. Bits: +# RIPV=0x1 EIPV=0x2 MCIP=0x4 LMCE_S=0x8 +# SRAO -> restart-safe context: MCIP|RIPV = 0x5, +LMCE_S = 0xD. +# SRAR -> current-instruction fault: MCIP|EIPV = 0x6, +LMCE_S = 0xE. +# Using the wrong flavor for SRAR (RIPV=1) makes the guest kernel treat +# the fault as restart-safe and skip SIGBUS.AR delivery. +MCG_STATUS_MCIP_RIPV = 0x5 +MCG_STATUS_MCIP_EIPV = 0x6 +MCG_STATUS_SRAO_LOCAL = 0xD +MCG_STATUS_SRAR_LOCAL = 0xE +# Back-compat alias for pre-existing callers that pass a "local SRAO" context. +MCG_STATUS_LOCAL = MCG_STATUS_SRAO_LOCAL + + +def install_msr_tools(test, session): + """Ensure rdmsr is available inside the guest.""" + if not utils_package.package_install("msr-tools", session): + test.cancel("Failed to install msr-tools inside guest.") + + +def rdmsr_hex(session, msr): + """Read a guest MSR via ``rdmsr`` and return its integer value. + + Uses ``cmd_status_output`` so a non-zero exit (msr module not loaded, + MSR unsupported on this CPU, etc.) surfaces as a ``RuntimeError`` with + the underlying rdmsr stderr instead of an opaque ``ValueError`` from + ``int("", 16)``. + """ + status, out = session.cmd_status_output("rdmsr -x0 0x%x" % msr) + out = out.strip() + if status != 0: + raise RuntimeError( + "rdmsr failed for MSR 0x%x (exit=%s): %s" % (msr, status, out) + ) + try: + return int(out, 16) + except ValueError: + raise RuntimeError( + "rdmsr returned non-hex output for MSR 0x%x: %r" % (msr, out) + ) + + +def detect_guest_lmce(session): + """Return ``"on"`` if all three LMCE-enabling bits are set in guest, else ``"off"``. + + Reads are done in dependency order and short-circuited: ``IA32_MCG_EXT_CTL`` + (0x4D0) is only architecturally present when ``IA32_MCG_CAP.LMCE_P`` is 1, + so ``MCG_CAP`` must be checked first. + """ + session.cmd("modprobe msr") + if not (rdmsr_hex(session, IA32_MCG_CAP) & MCG_LMCE_P_MASK): + return "off" + if not (rdmsr_hex(session, IA32_FEATURE_CONTROL) & FEATURE_LMCE_ON_MASK): + return "off" + if not (rdmsr_hex(session, IA32_MCG_EXT_CTL) & MCG_EXT_CTL_LMCE_EN_MASK): + return "off" + return "on" + + +def check_lmce_state(test, session, expected_state): + """Verify guest LMCE MSR state matches ``expected_state`` (``"on"`` or ``"off"``).""" + state = detect_guest_lmce(session) + if state != expected_state: + test.fail( + "Guest LMCE state %s does not match expected %s" % (state, expected_state) + ) + test.log.info("Guest LMCE state is '%s' as expected.", state) + + +def check_lmce_marker(test, dmesg, expected_state): + """Assert LMCE dmesg marker is consistent with ``expected_state``. + + ``lmce=off`` -> guest dmesg must NOT contain ``LMCE`` / ``Local Machine``. + This catches KVM incorrectly reporting a broadcast MCE as local. + ``lmce=on`` -> presence of the marker is desirable but not required. + The Linux kernel only prints the ``Local Machine Check Exception`` + banner when it takes the synchronous #MC handler path (AR/PANIC + severity). SRAO / recoverable events go through the deferred + workqueue path which emits only ``Machine check events logged``. + MSR-level LMCE readiness is already verified by check_lmce_state(); + here we only warn when the banner is absent. + """ + has_lmce = "LMCE" in dmesg or "Local Machine" in dmesg + if expected_state == "off" and has_lmce: + test.fail("lmce=off but guest dmesg shows LMCE marker: %s" % dmesg) + if expected_state == "on" and not has_lmce: + test.log.info( + "lmce=on: no explicit LMCE banner in dmesg (deferred-log path); " + "MSR state already confirmed. dmesg=%s", + dmesg, + ) + + +def build_victim(test, params, vm, session): + """Compile the ``victim`` helper from ``deps/ras/`` inside the guest. + + ``victim`` mmaps a page, prints its guest physical address, then either + consumes the page immediately (for SRAO) or polls a trigger file (for + SRAR / EINJ). Both lmce.py (HMP path) and ras.py (real EINJ path) need + this binary compiled at the same guest path. + + Returns the absolute path to the compiled binary inside the guest. + """ + source_file = params["source_file"] + exec_file = params["exec_file"] + test_dir = params["test_dir"] + deps_dir = virttest_data_dir.get_deps_dir("ras") + if not utils_package.package_install("gcc", session): + test.cancel("Failed to install gcc inside guest.") + vm.copy_files_to(os.path.join(deps_dir, source_file), test_dir) + compile_cmd = "cd %s && gcc %s -o %s" % (test_dir, source_file, exec_file) + if session.cmd_status(compile_cmd) != 0: + test.error("Failed to compile %s inside guest." % source_file) + session.cmd("rm -f %s/%s" % (test_dir, source_file)) + return os.path.join(test_dir, exec_file) + + +def parse_victim_gpa(session, log_path): + """Return the GPA printed by ``victim`` in ``log_path`` as an ``int``. + + ``victim`` prints ``physical address of (0xVA) = 0xGPA``; we anchor on + the ``physical address`` / ``=`` markers rather than a positional token + so extra banner lines do not break the parser. Returns ``None`` if the + log has not yet been written or the address cannot be decoded. + """ + out = session.cmd_output("cat %s 2>/dev/null || true" % log_path) + for line in out.splitlines(): + if "physical address" in line.lower() and "=" in line: + try: + return int(line.rsplit("=", 1)[-1].strip(), 0) + except ValueError: + continue + return None diff --git a/KVM/qemu/provider/nbench_utils.py b/KVM/qemu/provider/nbench_utils.py new file mode 100644 index 00000000..a0300b10 --- /dev/null +++ b/KVM/qemu/provider/nbench_utils.py @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +""" +Shared helpers for downloading, building, and running nbench-byte +inside a guest VM. +""" + +import re + + +_NBENCH_RESULT_RE = re.compile( + r"^\s*(NUMERIC SORT|STRING SORT|BITFIELD|FP EMULATION|FOURIER|" + r"ASSIGNMENT|IDEA|HUFFMAN|NEURAL NET|LU DECOMPOSITION)\b" +) + + +def prepare_nbench(session, guest_workdir, params): + """Download nbench tarball in guest, extract and build. + + Required params: ``nbench_url``, ``nbench_tarball``, + ``nbench_extracted_dir``. + """ + nbench_url = params["nbench_url"] + tarball = params["nbench_tarball"] + extracted = params["nbench_extracted_dir"] + + session.cmd("mkdir -p %s" % guest_workdir) + session.cmd("rm -rf %s/*" % guest_workdir, ignore_all_errors=True) + + session.cmd( + "cd %s && wget -q %s -O %s" % (guest_workdir, nbench_url, tarball), + timeout=120, + ) + session.cmd( + "cd %s && tar xzf %s" % (guest_workdir, tarball), timeout=60, + ) + session.cmd( + "cd %s/%s && sed -i 's/-static//' Makefile && make" + % (guest_workdir, extracted), + timeout=180, + ) + + +def run_nbench(session, guest_workdir, params, test, label="nbench"): + """Run nbench under a bounded timeout and validate result output. + + Required params: ``nbench_extracted_dir``, + ``nbench_max_runtime_seconds``, ``nbench_min_result_lines``. + + :param label: prefix for log/failure messages (e.g. ``"pre-lm"``). + """ + extracted = params["nbench_extracted_dir"] + max_seconds = int(params["nbench_max_runtime_seconds"]) + min_lines = int(params["nbench_min_result_lines"]) + + out = session.cmd_output( + "cd %s/%s && stdbuf -oL timeout %d ./nbench 2>&1" + % (guest_workdir, extracted, max_seconds), + timeout=max_seconds + 60, + ) + + result_lines = [ + line for line in out.splitlines() if _NBENCH_RESULT_RE.match(line) + ] + test.log.info( + "%s: nbench emitted %d result lines (need >= %d)", + label, len(result_lines), min_lines, + ) + for line in result_lines[:min_lines]: + test.log.info(" %s", line) + if len(result_lines) < min_lines: + test.fail( + "%s: nbench produced %d result lines; expected >= %d. " + "Tail of output:\n%s" + % ( + label, + len(result_lines), + min_lines, + "\n".join(out.splitlines()[-10:]), + ) + ) diff --git a/KVM/qemu/ras.cfg b/KVM/qemu/ras.cfg deleted file mode 100644 index 43a7da85..00000000 --- a/KVM/qemu/ras.cfg +++ /dev/null @@ -1,17 +0,0 @@ -- ras: - no Windows - type = ras - virt_test_type = qemu - vm_accelerator = kvm - force_create_image = no - remove_image = no - start_vm = yes - kill_vm = yes - auto_cpu_model = "no" - cpu_model = host - test_dir = '/tmp' - source_file = 'victim.c' - exec_file = 'victim' - variants: - - srao: - error_type = "0x10" diff --git a/KVM/qemu/smp.cfg b/KVM/qemu/smp.cfg index 86fad4d5..c9499975 100644 --- a/KVM/qemu/smp.cfg +++ b/KVM/qemu/smp.cfg @@ -3,10 +3,10 @@ type = smp virt_test_type = qemu vm_accelerator = kvm - force_create_image = no - remove_image = no start_vm = yes kill_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" auto_cpu_model = "no" cpu_model = host variants: diff --git a/KVM/qemu/stress_test.cfg b/KVM/qemu/stress_test.cfg new file mode 100644 index 00000000..ed18892d --- /dev/null +++ b/KVM/qemu/stress_test.cfg @@ -0,0 +1,21 @@ +- stress_test: + type = stress_test + virt_test_type = qemu + vm_accelerator = kvm + force_create_image = no + remove_image = no + image_snapshot = yes + start_vm = no + kill_vm = yes + shell_prompt = "^\[.*\][\#\$]\s*$" + vga = std + smp = 8 + mem = 8192 + stress_cpu = 8 + stress_vm_workers = 8 + stress_vm_bytes = "1024M" + stress_io = 1 + stress_hdd = 1 + stress_hdd_bytes = "3G" + stress_timeout = 60 + stress_tool = "stress-ng" diff --git a/KVM/qemu/td_debug.cfg b/KVM/qemu/td_debug.cfg index 85777e96..725ba867 100644 --- a/KVM/qemu/td_debug.cfg +++ b/KVM/qemu/td_debug.cfg @@ -6,6 +6,8 @@ vm_secure_guest_type = tdx start_vm = yes kill_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" vga = std auto_cpu_model = "no" cpu_model = host diff --git a/KVM/qemu/td_huge_resource.cfg b/KVM/qemu/td_huge_resource.cfg index 7ebb5a3a..0289b99a 100644 --- a/KVM/qemu/td_huge_resource.cfg +++ b/KVM/qemu/td_huge_resource.cfg @@ -5,6 +5,8 @@ machine_type_extra_params = "kernel-irqchip=split" vm_secure_guest_type = tdx start_vm = no + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" vga = std auto_cpu_model = "no" cpu_model = host diff --git a/KVM/qemu/tdx_QGS_socket.cfg b/KVM/qemu/tdx_QGS_socket.cfg new file mode 100644 index 00000000..273b5b02 --- /dev/null +++ b/KVM/qemu/tdx_QGS_socket.cfg @@ -0,0 +1,16 @@ +- tdx_QGS_socket: + type = tdx_QGS_socket + virt_test_type = qemu + vm_accelerator = kvm + machine_type_extra_params = "kernel-irqchip=split" + vm_secure_guest_type = tdx + vm_secure_guest_object_options = "quote-generation-socket.type=vsock quote-generation-socket.cid=2 quote-generation-socket.port=4050" + qemu_cmdline_format_cfg = string:{"secure_guest":{"object": "json"}} + start_vm = no + kill_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" + vga = std + auto_cpu_model = "no" + cpu_model = host + tsm_report_dir = "/sys/kernel/config/tsm/report/report0" diff --git a/KVM/qemu/tdx_basic.cfg b/KVM/qemu/tdx_basic.cfg index df267f0a..0b6c7bfd 100644 --- a/KVM/qemu/tdx_basic.cfg +++ b/KVM/qemu/tdx_basic.cfg @@ -4,12 +4,10 @@ vm_accelerator = kvm machine_type_extra_params = "kernel-irqchip=split" vm_secure_guest_type = tdx - # Don't create/remove guest images - force_create_image = no - remove_image = no start_vm = no # Stop VM after testing kill_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. shell_prompt = "^\[.*\][\#\$]\s*$" vga = std auto_cpu_model = "no" diff --git a/KVM/qemu/tdx_cpuoff_pinedVMdown.cfg b/KVM/qemu/tdx_cpuoff_pinedVMdown.cfg index 1ec99557..50f98d70 100644 --- a/KVM/qemu/tdx_cpuoff_pinedVMdown.cfg +++ b/KVM/qemu/tdx_cpuoff_pinedVMdown.cfg @@ -3,11 +3,12 @@ virt_test_type = qemu machine_type_extra_params = "kernel-irqchip=split" vm_secure_guest_type = tdx - # Don't create/remove guest images smp = 64 start_vm = no # Stop VM after testing kill_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" auto_cpu_model = "no" cpu_model = host guest_flags = "tdx_guest" diff --git a/KVM/qemu/tdx_disable.cfg b/KVM/qemu/tdx_disable.cfg index 989a2da8..b1ca2d8d 100644 --- a/KVM/qemu/tdx_disable.cfg +++ b/KVM/qemu/tdx_disable.cfg @@ -5,6 +5,8 @@ machine_type_extra_params = "kernel-irqchip=split" vm_secure_guest_type = tdx start_vm = no + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" vga = std auto_cpu_model = "no" cpu_model = host diff --git a/KVM/qemu/tdx_hdevents_in5sec.cfg b/KVM/qemu/tdx_hdevents_in5sec.cfg new file mode 100644 index 00000000..6a789bea --- /dev/null +++ b/KVM/qemu/tdx_hdevents_in5sec.cfg @@ -0,0 +1,20 @@ +- tdx_hdevents_in5sec: + type = tdx_hdevents_in5sec + virt_test_type = qemu + vm_accelerator = kvm + machine_type_extra_params = "kernel-irqchip=split" + vm_secure_guest_type = tdx + start_vm = no + kill_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" + vga = std + auto_cpu_model = "no" + cpu_model = host + cpu_model_flags = ",pmu=on" + perf_events = "cache-misses,instructions,branch-misses,bus-cycles,branch-instructions,L1-dcache-loads" + perf_duration = 5 + perf_expected_output = "Performance counter stats" + pre_command = "echo 0 > /proc/sys/kernel/nmi_watchdog && modprobe -r kvm_intel && modprobe kvm_intel enable_mediated_pmu=Y" + post_command = "echo 1 > /proc/sys/kernel/nmi_watchdog && modprobe -r kvm_intel && modprobe kvm_intel" + post_command_noncritical = yes diff --git a/KVM/qemu/tdx_max_guests.cfg b/KVM/qemu/tdx_max_guests.cfg index 8b8b4788..1c5a5fb7 100644 --- a/KVM/qemu/tdx_max_guests.cfg +++ b/KVM/qemu/tdx_max_guests.cfg @@ -6,6 +6,8 @@ vm_secure_guest_type = tdx image_snapshot = yes start_vm = no + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" vga = std auto_cpu_model = "no" cpu_model = host diff --git a/KVM/qemu/tdx_memory_confident.cfg b/KVM/qemu/tdx_memory_confident.cfg index 1d0abe7c..60df02da 100644 --- a/KVM/qemu/tdx_memory_confident.cfg +++ b/KVM/qemu/tdx_memory_confident.cfg @@ -4,10 +4,11 @@ # Too large memory will lead to python asyncio exception. mem = 1024 machine_type_extra_params = "kernel-irqchip=split" - # Don't create/remove guest images start_vm = no # Stop VM after testing kill_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" auto_cpu_model = "no" cpu_model = host dump_memory_path = "/tmp/memory-dump" diff --git a/KVM/qemu/tdx_numa_set.cfg b/KVM/qemu/tdx_numa_set.cfg new file mode 100644 index 00000000..7d461b0b --- /dev/null +++ b/KVM/qemu/tdx_numa_set.cfg @@ -0,0 +1,24 @@ +- tdx_numa_set: + type = boot_check + virt_test_type = qemu + vm_accelerator = kvm + machine_type_extra_params = "kernel-irqchip=split" + vm_secure_guest_type = tdx + start_vm = no + kill_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" + vga = std + auto_cpu_model = "no" + cpu_model = host + smp = 4 + mem = 4096 + mem_devs = "mem0" + use_mem_mem0 = "no" + size_mem_mem0 = 4096M + backend_mem_mem0 = memory-backend-ram + prealloc_mem_mem0 = yes + guest_numa_nodes = "node0" + numa_memdev_node0 = mem-mem0 + numa_cpus_node0 = "0-3" + numa_nodeid_node0 = 0 diff --git a/KVM/qemu/tdx_seam_module.cfg b/KVM/qemu/tdx_seam_module.cfg index 49263b25..dea2f838 100644 --- a/KVM/qemu/tdx_seam_module.cfg +++ b/KVM/qemu/tdx_seam_module.cfg @@ -4,17 +4,15 @@ vm_accelerator = kvm machine_type_extra_params = "kernel-irqchip=split" vm_secure_guest_type = tdx - # Don't create/remove guest images - force_create_image = no - remove_image = no start_vm = no # Stop VM after testing kill_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. shell_prompt = "^\[.*\][\#\$]\s*$" vga = std auto_cpu_model = "no" cpu_model = host read_cmd = "cat /sys/module/kvm_intel/parameters/%s" rdmsr_cmd = "rdmsr 0xfe --bitfield 15:15" - tdx_module_pattern = "tdx: module initialized" + tdx_module_pattern = "tdx: TDX-Module initialized" tdx_negative_pattern = "No TDX module loaded by BIOS" diff --git a/KVM/qemu/tdx_ve_reduction.cfg b/KVM/qemu/tdx_ve_reduction.cfg new file mode 100644 index 00000000..a1a3c00a --- /dev/null +++ b/KVM/qemu/tdx_ve_reduction.cfg @@ -0,0 +1,69 @@ +- tdx_ve_reduction: + type = tdx_ve_reduction + virt_test_type = qemu + vm_accelerator = kvm + machine_type_extra_params = "kernel-irqchip=split" + vm_secure_guest_type = tdx + start_vm = no + kill_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" + auto_cpu_model = "no" + cpu_model = host + deps_subdir = "tdx_ve_reduction" + tdx_compliance_src_subdir = "tdx-compliance" + guest_test_dir = "/root/tdx-ve-reduction" + case_action = compliance + variants: + - tdx_ve_enabled: + case_action = ve_check + + - tdx_ve_cpuid_default: + caselist = tdx_cpuid_default.h + + - tdx_ve_cpuid_cpu_tp1: + caselist = tdx_cpuid_tp1.h + smp = 16 + vcpu_sockets = 1 + vcpu_dies = 2 + vcpu_threads = 2 + vcpu_cores = 4 + cpu_pin = 0 + + - tdx_ve_cpuid_cpu_tp2: + caselist = tdx_cpuid_tp2.h + smp = 12 + vcpu_sockets = 1 + vcpu_threads = 3 + vcpu_cores = 4 + cpu_pin = 11 + + - tdx_ve_cpuid_cpu_tp3: + caselist = tdx_cpuid_tp3.h + smp = 48 + vcpu_sockets = 2 + vcpu_dies = 2 + vcpu_threads = 3 + vcpu_cores = 4 + cpu_pin = 47 + + - tdx_ve_back_feature_off: + caselist = tdx_cpuid_back_feature_off.h + cpu_model_flags = ",-est,-tsc-deadline,-mce,-mtrr,-mca,-acpi,-core-capability,-dca" + + - tdx_ve_back_feature_on: + caselist = tdx_cpuid_back_feature_on.h + cpu_model_flags = ",+est,+tsc-deadline,+mce,+mtrr,+mca,+acpi,+core-capability,+dca" + + - tdx_ve_back_feature_on_ve: + case_action = compliance_ve_series + ve_case_headers = "tdx_cpuid_ve1.h tdx_cpuid_ve2.h tdx_cpuid_ve3.h tdx_cpuid_ve4.h tdx_cpuid_ve5.h tdx_cpuid_ve6.h" + cpu_model_flags = ",+est,+tsc-deadline,+mce,+mtrr,+mca,+acpi,+core-capability,+dca" + + - tdx_ve_ctls_feature_off: + caselist = tdx_cpuid_ctls_feature_off.h + cpu_model_flags = ",-est,-tsc-deadline,-mce,-mtrr,-mca,-acpi,-core-capability,-dca" + + - tdx_ve_ctls_feature_on: + caselist = tdx_cpuid_ctls_feature_on.h + cpu_model_flags = ",+est,+tsc-deadline,+mce,+mtrr,+mca,+acpi,+core-capability,+dca" diff --git a/KVM/qemu/tdx_without_nographic.cfg b/KVM/qemu/tdx_without_nographic.cfg new file mode 100644 index 00000000..d33aa6ec --- /dev/null +++ b/KVM/qemu/tdx_without_nographic.cfg @@ -0,0 +1,16 @@ +- tdx_without_nographic: + type = tdx_without_nographic + virt_test_type = qemu + machine_type_extra_params = "kernel-irqchip=split" + vm_secure_guest_type = tdx + start_vm = no + # Stop VM after testing + kill_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" + auto_cpu_model = "no" + cpu_model = host + guest_flags = "tdx_guest" + # Comment all `display` parameters in tdx_temp.cfg and change this + # parameter among (vnc, sdl, spice) according to your machine config + display = "vnc" diff --git a/KVM/qemu/tests/boot_check.py b/KVM/qemu/tests/boot_check.py index 7c8787f3..b55afbf2 100644 --- a/KVM/qemu/tests/boot_check.py +++ b/KVM/qemu/tests/boot_check.py @@ -54,13 +54,17 @@ def run(test, params, env): vcpus = params.get_numeric("smp") if vm.get_cpu_count() != vcpus: test.fail("CPU number in guest is not same as configured vcpus number") - is_max_mem = params.get_boolean("is_max_mem") - if is_max_mem: - memory = utils_misc.get_usable_memory_size() - else: - memory = params.get_numeric("mem") - if vm.get_totalmem_sys()//1024 != memory: - test.fail("Memory in guest is not same as configured") + # Some memory sizes are not aligned to the guest memory block size, so + # the in-guest total memory can not match the configured value exactly. + # For those variants only boot success is required, skip the mem check. + if params.get_boolean("check_mem", True): + is_max_mem = params.get_boolean("is_max_mem") + if is_max_mem: + memory = utils_misc.get_usable_memory_size() + else: + memory = params.get_numeric("mem") + if vm.get_totalmem_sys()//1024 != memory: + test.fail("Memory in guest is not same as configured") is_nxhp = params.get_boolean("is_nxhp") if is_nxhp: is_high_mem_nxhp = params.get_boolean("is_high_mem_nxhp") diff --git a/KVM/qemu/tests/boot_win11.py b/KVM/qemu/tests/boot_win11.py new file mode 100644 index 00000000..1a3634fb --- /dev/null +++ b/KVM/qemu/tests/boot_win11.py @@ -0,0 +1,105 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +# Author: Kai Zhang +# +# History: Jun. 2026 - Kai Zhang - creation + +import re +import base64 +import time + +from provider import dmesg_router # pylint: disable=unused-import +from virttest import env_process, error_context, guest_agent + + +def _append_param(params, key, value): + values = params.get(key, "").split() + if value not in values: + values.append(value) + params[key] = " ".join(values) + + +def get_cmd_output(gagent, exe, args=None): + pid = gagent.cmd( + cmd="guest-exec", + args={ + "path": exe, + "arg": args, + "capture-output": True, + }, + )["pid"] + while True: + status = gagent.guest_exec_status(pid) or {} + if status.get("exited"): + break + time.sleep(1) + + log = base64.b64decode(status.get("out-data", "")).decode("utf-8", errors="replace") + return log + + +@error_context.context_aware +def run(test, params, env): + """ + Windows 11 host boot test: + 1. Boot VM with windows 11 image and login through qemu guest agent + 2. Get os version related information + 3. Get driver related information + 4. Destroy VM + + Windows host cannot use session to run command, use qemu guest agent. + + :param test: QEMU test object + :param params: Dictionary with the test parameters + :param env: Dictionary with test environment. + """ + params["start_vm"] = 'yes' + params["bios_path"] = params.get("boot_win11_bios_path", "") + gagent_name = params.get("gagent_name", "org.qemu.guest_agent.0") + _append_param(params, "serials", gagent_name) + params["serial_type_%s" % gagent_name] = "virtserialport" + params["serial_name_%s" % gagent_name] = gagent_name + env_process.preprocess_vm(test, params, env, params["main_vm"]) + vm = env.get_vm(params["main_vm"]) + vm.verify_alive() + + drivers_keywords = params.get("drivers_keywords", "VirtIO vio").split() + drivers_pattern = "|".join(drivers_keywords) + + error_context.context("Connect to QEMU guest agent.", test.log.info) + filename = vm.get_serial_console_filename(gagent_name) + gagent_params = params.object_params(gagent_name) + gagent_params["monitor_filename"] = filename + + gagent = guest_agent.QemuAgent( + vm, gagent_name, "virtio", gagent_params, get_supported_cmds=True) + gagent.verify_responsive() + + error_context.context("Get OS version and name.", test.log.info) + output = get_cmd_output(gagent, "cmd.exe", ["/c", "ver"]) + test.log.info("%s", output) + output = get_cmd_output(gagent, "cmd.exe", ["/c", "wmic os get Name"]) + test.log.info("%s", output) + + error_context.context("Get driver version information in guest.", test.log.info) + system_drivers = get_cmd_output(gagent, "cmd.exe", ["/c", "wmic sysdriver get DisplayName,PathName"]) + test.log.debug("Drivers exist in the system:\n %s", system_drivers) + + test.log.info(system_drivers) + + for driver in system_drivers.splitlines(): + if re.findall(drivers_pattern, driver, re.I): + driver_info = driver.strip().split() + driver_name = " ".join(driver_info[:-1]) + path = driver_info[-1] + path = re.sub(r"\\", "\\\\\\\\", path) + driver_ver_cmd = "wmic datafile where name=" + driver_ver_cmd += "'%s' get version" % path + output = get_cmd_output(gagent, "cmd.exe", ["/c", driver_ver_cmd]) + msg = "Driver %s" % driver_name + msg += " version is %s" % output.strip().split()[-1] + test.log.info(output) + test.log.info(msg) diff --git a/KVM/qemu/tests/buslock_ve.py b/KVM/qemu/tests/buslock_ve.py new file mode 100644 index 00000000..5771d351 --- /dev/null +++ b/KVM/qemu/tests/buslock_ve.py @@ -0,0 +1,293 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +import os +import time + +from provider import dmesg_router # pylint: disable=unused-import +from provider import kvm_trace_utils + +from avocado.utils import process +from virttest import data_dir, env_process, error_context, utils_package + + +HOST_TRACE_FILE = kvm_trace_utils.HOST_TRACE_FILE + +# Bit 26 of exit_reason (Intel SDM Vol.3C 27.2) is the bus-lock flag. This +# test matches only dedicated BUS_LOCK basic-reason (74) vmexits via +# ``grep BUS_LOCK`` on the kvm_exit trace, then additionally assert bit 26 +# is set in the extra-flags hex of that record. +# The kernel-side ftrace filter below is a defensive optimisation only: it +# suppresses unrelated kvm_exit lines so the tiny per-CPU trace ring buffer +# cannot wrap and drop the BUS_LOCK record on a noisy host. It does not +# change the assertion (``grep BUS_LOCK`` still only sees Case A because +# Case B rows print the other basic reason name, not "BUS_LOCK"). +BUS_LOCK_EXIT_REASON_MASK = 0x4000000 +BUS_LOCK_FILTER = "exit_reason & 0x%x" % BUS_LOCK_EXIT_REASON_MASK + + +def _check_host_bus_lock_msr(test, params): + """Ensure host CPU exposes BUS_LOCK_DETECT via IA32_VMX_PROCBASED_CTLS2.""" + process.run("modprobe msr", shell=True, ignore_status=True) + probe_cmd = params["host_msr_probe"] + result = process.run(probe_cmd, shell=True, ignore_status=True) + if result.exit_status != 0 or result.stdout_text.strip() != "1": + test.cancel( + "Host does not report BUS_LOCK_DETECT support: %s" + % probe_cmd + ) + + +def _install_guest_gcc(test, session): + """Ensure gcc is available inside the guest for compiling the bus-lock tool.""" + if not utils_package.package_install("gcc", session): + test.cancel("Failed to install gcc in guest") + + +def _prepare_guest_bus_lock_tool(test, params, vm, session, source_file, + exec_file): + """Copy and compile a bus-lock helper in the guest; return its guest path.""" + deps_subdir = params["deps_subdir"] + test_dir = params["test_dir"] + deps_dir = data_dir.get_deps_dir(deps_subdir) + + files_to_copy = [source_file] + shared_header = params.get("bus_lock_common_file") + if shared_header: + files_to_copy.append(shared_header) + + for name in files_to_copy: + vm.copy_files_to(os.path.join(deps_dir, name), test_dir) + + compile_cmd = "cd %s && gcc %s -o %s" % (test_dir, source_file, exec_file) + if session.cmd_status(compile_cmd) != 0: + test.error("Failed to compile %s inside guest" % source_file) + session.cmd("rm -f %s/%s" % (test_dir, " ".join(files_to_copy))) + return os.path.join(test_dir, exec_file) + + +def _verify_host_bus_lock_exit(test): + """a BUS_LOCK vmexit occurred with bit 26 set. + + 1. ``grep BUS_LOCK`` on host kvm_exit trace -- only matches the + dedicated BUS_LOCK basic-reason (74) vmexit, not bus locks that + happen to accompany another exit reason. + 2. Parse the exit_reason extra-flags hex token that follows + ``BUS_LOCK`` on that trace line. + 3. Assert bit 26 (``0x4000000``) is set in that value. + Raises ``test.fail`` if any step is not satisfied. + """ + result = process.run( + "grep -m 1 BUS_LOCK %s || true" % HOST_TRACE_FILE, + shell=True, ignore_status=True, + ) + line = result.stdout_text.strip() + if not line: + test.fail("No BUS_LOCK VM exit observed in host kvm_exit trace") + tokens = line.split("BUS_LOCK", 1)[1].split() + if not tokens: + test.fail("No exit_reason flags after BUS_LOCK in trace: %r" % line) + flags_str = tokens[0] + try: + flags = int(flags_str, 16) + except ValueError: + test.fail( + "Cannot parse exit_reason flags %r from BUS_LOCK trace: %r" + % (flags_str, line) + ) + test.log.info("Host BUS_LOCK vmexit exit_reason flags: %s", flags_str) + if not flags & BUS_LOCK_EXIT_REASON_MASK: + test.fail( + "BUS_LOCK vmexit captured but bit 26 not set in exit_reason %s" + % flags_str + ) + + +def _boot_vm(test, params, env): + """Boot the main VM using the current params snapshot.""" + params["start_vm"] = "yes" + env_process.preprocess_vm(test, params, env, params["main_vm"]) + vm = env.get_vm(params["main_vm"]) + vm.verify_alive() + return vm + + +def _run_bus_lock_exit(test, params, env): + """Verify a BUS_LOCK VM exit is recorded on host.""" + vm = _boot_vm(test, params, env) + try: + session = vm.wait_for_login( + timeout=params.get_numeric("login_timeout", 240) + ) + try: + _install_guest_gcc(test, session) + exec_path = _prepare_guest_bus_lock_tool( + test, params, vm, session, + params["bus_lock_source_file"], + params["bus_lock_exec_file"], + ) + kvm_trace_utils.enable_kvm_exit_trace( + test, filter_expr=BUS_LOCK_FILTER) + try: + error_context.context( + "Trigger a bus lock inside guest", test.log.info, + ) + session.cmd_status(exec_path, + timeout=params.get_numeric( + "bus_lock_run_timeout", 30)) + time.sleep(params.get_numeric("trace_settle_time", 3)) + _verify_host_bus_lock_exit(test) + finally: + kvm_trace_utils.disable_kvm_exit_trace(clear_filter=True) + finally: + session.close() + finally: + vm.destroy(gracefully=False) + + +def _measure_bus_lock_count(test, params, env, run_label, machine_extra): + """Boot a VM with the given machine params, run bus_lock_ct in guest, + sample host perf-kvm counters, return the second-interval count.""" + run_params = params.copy() + run_params["machine_type_extra_params"] = machine_extra + + error_context.context( + "Boot VM for '%s' run (machine_type_extra_params=%r)" + % (run_label, machine_extra), + test.log.info, + ) + vm = _boot_vm(test, run_params, env) + try: + session = vm.wait_for_login( + timeout=run_params.get_numeric("login_timeout", 240) + ) + try: + _install_guest_gcc(test, session) + exec_path = _prepare_guest_bus_lock_tool( + test, run_params, vm, session, + run_params["bus_lock_source_file"], + run_params["bus_lock_exec_file"], + ) + error_context.context( + "Start bus_lock_ct in guest background", test.log.info, + ) + session.cmd("nohup %s >/dev/null 2>&1 &" % exec_path) + # Give the guest a moment to enter its hot loop so the host perf + # window actually captures bus-lock events. + time.sleep(run_params.get_numeric("bus_lock_settle_time", 2)) + + perf_cmd = ( + "perf stat -e %s -a -I %d --interval-count %d" + % ( + run_params["perf_event"], + run_params.get_numeric("perf_interval_ms", 1000), + run_params.get_numeric("perf_interval_count", 2), + ) + ) + error_context.context( + "Host: %s" % perf_cmd, test.log.info, + ) + result = process.run(perf_cmd, shell=True, ignore_status=True) + perf_output = (result.stdout_text or "") + \ + (result.stderr_text or "") + test.log.info("perf output for %s:\n%s", run_label, perf_output) + + count = _parse_second_interval_count(test, perf_output, run_label) + test.log.info("%s bus-lock count: %d", run_label, count) + return count + finally: + session.close() + finally: + vm.destroy(gracefully=False) + + +def _parse_second_interval_count(test, perf_output, run_label): + """Extract the counter value from the 2nd data line of perf-stat output.""" + data_lines = [] + for line in perf_output.splitlines(): + stripped = line.strip() + if not stripped or stripped.startswith("#"): + continue + parts = stripped.split() + if len(parts) < 2: + continue + try: + float(parts[0]) + except ValueError: + continue + data_lines.append(parts) + if len(data_lines) < 2: + test.error( + "perf output for %s has fewer than 2 interval rows" % run_label + ) + raw = data_lines[1][1].replace(",", "") + if raw in ("/dev/null || echo x" % topo)) + die_ids.add(run_cmd("cat %s/die_id 2>/dev/null || echo x" % topo)) + socket_ids.add(run_cmd("cat %s/physical_package_id" % topo)) + + if len(core_ids) == 1: + scopes[index] = 1 + elif len(cluster_ids) == 1: + scopes[index] = 2 + elif len(die_ids) == 1: + scopes[index] = 3 + elif len(socket_ids) == 1: + scopes[index] = 4 + else: + scopes[index] = 0 # unknown + + return scopes + + +def _parse_guest_cpuid_cache(session): + """ + Parse ``cpuid -1`` output in guest for cache 0..3 sharing/assoc fields. + + Extracts the hex value of: + - "maximum IDs for CPUs sharing cache" (CPUID.4:EAX[25:14] + 1) + - "ways of associativity" (CPUID.4:EBX[31:22] + 1) + within each ``--- cache N ---`` block, for N in 0..3. + + :param session: guest session + :return: dict {index: {"sharing": "0x1", "assoc": "0x8"}}, or None if + the ``cpuid`` command failed or returned no cache blocks. + """ + status, output = session.cmd_status_output("cpuid -1", timeout=60) + if status != 0 or not output.strip(): + return None + + result = {} + current = None + header_re = re.compile(r"^\s*---\s*cache\s+(\d+)\s*---\s*$") + # Value token after '=' is the first whitespace-separated field. + val_re = re.compile(r"=\s*(\S+)") + for line in output.splitlines(): + m = header_re.match(line) + if m: + idx = int(m.group(1)) + current = idx if 0 <= idx <= 3 else None + if current is not None: + result.setdefault(current, {}) + continue + if current is None: + continue + if "maximum IDs for CPUs sharing cache" in line: + mv = val_re.search(line) + if mv: + result[current]["sharing"] = mv.group(1) + elif "ways of associativity" in line: + mv = val_re.search(line) + if mv: + result[current]["assoc"] = mv.group(1) + return result or None + + +def _hex_eq(a, b): + """Compare two hex strings (e.g. '0x1f' vs '0x1F') by numeric value.""" + try: + return int(a, 16) == int(b, 16) + except (TypeError, ValueError): + return False + + +@error_context.context_aware +def run(test, params, env): + """ + Compare host and guest cache share scope. + + Steps: + 1. Collect host cache share scope for each cache index. + 2. Boot guest with configured CPU model and SMP topology. + 3. Collect guest cache share scope. + 4. Compare and fail if any mismatch. + + :param test: QEMU test object + :param params: Dictionary with the test parameters + :param env: Dictionary with test environment + """ + error_context.context("Collect host cache share scope", test.log.info) + host_scopes = _get_cache_share_scope() + test.log.info("Host cache scopes: %s", host_scopes) + if not host_scopes: + test.cancel("Could not determine host cache topology") + + # Force SMP topology. avocado-vt's cartesian config loads guest-hw.cfg + # (which owns the 'smp2' variant) AFTER subtests.cfg, so any 'smp = N' + # set in the cfg gets clobbered by the outer smp variant. Override here + # so QEMU sees the topology we actually want: + # sockets * dies * modules * cores * threads = 2*1*2*16*2 = 128 + # avocado-vt has no 'modules' knob, so 'modules=2' is appended via + # extra_params; QEMU merges both -smp options at parse time. + smp = params.get_numeric("vcpu_maxcpus") or 128 + params["smp"] = str(smp) + params["vcpu_maxcpus"] = str(smp) + params.setdefault("vcpu_dies", "1") + + params["start_vm"] = "yes" + vm_name = params["main_vm"] + env_process.preprocess_vm(test, params, env, vm_name) + vm = env.get_vm(vm_name) + vm.verify_alive() + + login_timeout = params.get_numeric("login_timeout", 240) + session = vm.wait_for_login(timeout=login_timeout) + try: + error_context.context("Collect guest cache share scope", test.log.info) + guest_scopes = _get_cache_share_scope(session) + test.log.info("Guest cache scopes: %s", guest_scopes) + + if not guest_scopes: + test.fail("Could not determine guest cache topology") + + mismatches = [] + for idx in host_scopes: + if idx not in guest_scopes: + mismatches.append( + "cache index %d: present on host (scope=%d) but missing in guest" + % (idx, host_scopes[idx]) + ) + elif host_scopes[idx] != guest_scopes[idx]: + mismatches.append( + "cache index %d: host scope=%d, guest scope=%d" + % (idx, host_scopes[idx], guest_scopes[idx]) + ) + + if mismatches: + test.fail("Cache topology mismatch:\n " + "\n ".join(mismatches)) + test.log.info("Cache topology matches between host and guest") + + expect_raw = params.get("cpuid_cache_expect", "").strip() + if expect_raw: + error_context.context( + "Verify guest CPUID.4 cache sharing / associativity", + test.log.info, + ) + tokens = expect_raw.split() + if len(tokens) != 8: + test.error( + "cpuid_cache_expect must have 8 hex tokens " + "(c0_sharing c0_assoc ... c3_sharing c3_assoc), got: %s" + % expect_raw + ) + expect = { + i: {"sharing": tokens[i * 2], "assoc": tokens[i * 2 + 1]} + for i in range(4) + } + + # Ensure the `cpuid` tool is available in guest. + cpuid_pkg = params.get("cpuid_pkg", "cpuid") + if session.cmd_status("which cpuid") != 0: + if not utils_package.package_install(cpuid_pkg, session): + test.cancel( + "Failed to install '%s' package in guest; " + "cannot verify CPUID.4 cache descriptors" % cpuid_pkg + ) + if session.cmd_status("which cpuid") != 0: + test.error( + "'cpuid' tool still not found in guest after " + "installing package '%s'" % cpuid_pkg + ) + + guest_cpuid = _parse_guest_cpuid_cache(session) + if guest_cpuid is None: + test.error( + "Failed to run 'cpuid -1' in guest or output was empty" + ) + test.log.info("Guest CPUID.4 cache info: %s", guest_cpuid) + + cpuid_mismatches = [] + for idx in range(4): + got = guest_cpuid.get(idx) + if not got: + cpuid_mismatches.append( + "cache %d: missing from guest cpuid output" % idx + ) + continue + for field in ("sharing", "assoc"): + exp_val = expect[idx][field] + got_val = got.get(field) + if not _hex_eq(exp_val, got_val): + cpuid_mismatches.append( + "cache %d %s: expected %s, got %s" + % (idx, field, exp_val, got_val) + ) + if cpuid_mismatches: + test.fail( + "Guest CPUID.4 cache info mismatch:\n " + + "\n ".join(cpuid_mismatches) + ) + test.log.info("Guest CPUID.4 cache info matches expected values") + finally: + session.close() diff --git a/KVM/qemu/tests/cpu_aia.py b/KVM/qemu/tests/cpu_aia.py new file mode 100644 index 00000000..4856d223 --- /dev/null +++ b/KVM/qemu/tests/cpu_aia.py @@ -0,0 +1,67 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2024 Intel Corporation + +# Author: Kai Zhang +# +# History: May. 2026 - Kai Zhang - creation + + +from pathlib import Path + +from provider import dmesg_router # pylint: disable=unused-import +from avocado.utils import process +from virttest import env_process, error_context +from virttest import utils_package + + +def aia_test(vm, test, session, command): + if not utils_package.package_install("gcc", session): + test.cancel("Failed to install package gcc.") + if not utils_package.package_install("make", session): + test.cancel("Failed to install package make.") + + script_dir = Path(__file__).resolve().parent + aia_deps_path = f"{script_dir}/../deps/aia" + + vm.copy_files_to(f"{aia_deps_path}/test.c", "~/") + vm.copy_files_to(f"{aia_deps_path}/Makefile", "~/") + vm.copy_files_to(f"{aia_deps_path}/libaia.h", "~/") + + session.cmd("cd ~ && make", ignore_all_errors=True) + session.cmd(f"~/test {command} > ~/aia_test.log 2>&1", ignore_all_errors=True) + + vm.copy_files_from("~/aia_test.log", "/tmp/aia_test.log") + with open("/tmp/aia_test.log", "r") as f: + if "not supported" in f.read(): + test.fail(f"{command} not supported") + session.cmd("rm -f ~/aia_test.log", ignore_all_errors=True) + process.system("rm -f /tmp/aia_test.log") + return + + +@error_context.context_aware +def run(test, params, env): + """ + Test AIA related CPU features in VM: + 1. Boot VM + 2. Copy dependency script to VM and compile + 3. Run executable in VM and check whether selected AIA is enabled + 3. Destroy VM + + :param test: QEMU test object + :param params: Dictionary with the test parameters + :param env: Dictionary with test environment. + """ + params["start_vm"] = 'yes' + env_process.preprocess_vm(test, params, env, params["main_vm"]) + vm = env.get_vm(params["main_vm"]) + vm.verify_alive() + timeout = params.get_numeric("login_timeout", 240) + session = vm.wait_for_login(timeout=timeout) + + command = params["test_command"] + aia_test(vm, test, session, command) + + session.close() diff --git a/KVM/qemu/tests/cpuid_1f.py b/KVM/qemu/tests/cpuid_1f.py new file mode 100644 index 00000000..6d0e6002 --- /dev/null +++ b/KVM/qemu/tests/cpuid_1f.py @@ -0,0 +1,85 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +import os + +from provider import dmesg_router # pylint: disable=unused-import +from avocado.utils import process +from virttest import data_dir, env_process, error_context, utils_package + + +@error_context.context_aware +def run(test, params, env): + """Validate CPUID leaf 0x1F topology information. + + The host variant runs the checker directly. Guest variants boot the + configured CPU model and topology, then run the checker inside the guest. + + :param test: QEMU test object + :param params: Dictionary with the test parameters + :param env: Dictionary with test environment + """ + host_only = params.get_boolean("host_only") + source_file = params.get("source_file", "cpuid_1f_check.c") + exec_file = params.get("exec_file", "cpuid_1f_check") + deps_dir = os.path.join(data_dir.get_deps_dir("cpuid_1f")) + src_path = os.path.join(deps_dir, source_file) + test_dir = params.get("test_dir", "/home") + + def _compile_and_run_host(): + error_context.context("Compile and run CPUID 0x1F check on host", test.log.info) + host_bin = os.path.join(deps_dir, exec_file) + compile_cmd = "gcc -o %s %s" % (host_bin, src_path) + result = process.run(compile_cmd, shell=True) + if result.exit_status: + test.error("Failed to compile %s: %s" % (source_file, result.stderr_text)) + try: + result = process.run(host_bin, shell=True) + if result.exit_status: + test.fail("Host %s failed: %s" % (exec_file, result.stdout_text)) + test.log.info( + "Host CPUID 0x1F check passed: %s", result.stdout_text.strip() + ) + finally: + process.system("rm -f %s" % host_bin, shell=True, ignore_status=True) + + def _compile_and_run_guest(): + error_context.context( + "Boot VM and run CPUID 0x1F check in guest", test.log.info + ) + params["start_vm"] = "yes" + vm_name = params["main_vm"] + env_process.preprocess_vm(test, params, env, vm_name) + vm = env.get_vm(vm_name) + vm.verify_alive() + session = vm.wait_for_login() + try: + if not utils_package.package_install("gcc", session): + test.cancel("Failed to install gcc in guest.") + vm.copy_files_to(src_path, test_dir) + guest_src = os.path.join(test_dir, source_file) + guest_bin = os.path.join(test_dir, exec_file) + compile_cmd = "gcc -o %s %s" % (guest_bin, guest_src) + error_context.context("Compile %s in guest" % source_file, test.log.info) + status, output = session.cmd_status_output(compile_cmd, timeout=60) + if status: + test.error("Failed to compile %s in guest: %s" % (source_file, output)) + error_context.context("Execute %s in guest" % exec_file, test.log.info) + status, output = session.cmd_status_output(guest_bin, timeout=60) + if status: + test.fail("Guest CPUID 0x1F check failed: %s" % output) + test.log.info("Guest CPUID 0x1F check passed: %s", output.strip()) + finally: + session.cmd( + "rm -f %s/%s %s/%s" % (test_dir, source_file, test_dir, exec_file), + ignore_all_errors=True, + ) + session.close() + vm.destroy(gracefully=False) + + if host_only: + _compile_and_run_host() + else: + _compile_and_run_guest() diff --git a/KVM/qemu/tests/cpuid_extended.py b/KVM/qemu/tests/cpuid_extended.py new file mode 100644 index 00000000..712b9e1f --- /dev/null +++ b/KVM/qemu/tests/cpuid_extended.py @@ -0,0 +1,93 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +""" +Verify guest CPUID extended leaves return expected values. + +Boots a guest and checks specific CPUID leaves (e.g. 0x80000005, +0x80000006) against expected register values configured in params. +""" + +from provider import dmesg_router # pylint: disable=unused-import +from virttest import env_process +from virttest import error_context + + +def _read_cpuid_leaf(session, leaf): + """ + Read a CPUID leaf inside the guest using cpuid command. + + :param session: guest login session + :param leaf: leaf number as string (e.g. "0x80000005") + :return: dict with keys eax, ebx, ecx, edx as integers + """ + output = session.cmd_output("cpuid -1 -r -l %s" % leaf).strip() + regs = {"eax": 0, "ebx": 0, "ecx": 0, "edx": 0} + for line in output.splitlines(): + if "eax=" not in line: + continue + # Parse line like: 0x80000005 0x00: eax=0x00000000 ebx=... + for reg in regs: + idx = line.find("%s=" % reg) + if idx >= 0: + start = idx + len(reg) + 1 + val_str = line[start:].split()[0] + regs[reg] = int(val_str, 16) + break # only first subleaf + return regs + + +@error_context.context_aware +def run(test, params, env): + """ + Check guest CPUID extended leaf register values. + + Steps: + 1. Boot guest with configured CPU model. + 2. Read the specified CPUID leaf inside guest. + 3. Compare each register against expected values. + + :param test: QEMU test object + :param params: Dictionary with the test parameters + :param env: Dictionary with test environment + """ + cpuid_leaf = params["cpuid_leaf"] + login_timeout = params.get_numeric("login_timeout", 240) + + params["start_vm"] = "yes" + vm_name = params["main_vm"] + env_process.preprocess_vm(test, params, env, vm_name) + vm = env.get_vm(vm_name) + vm.verify_alive() + + session = vm.wait_for_login(timeout=login_timeout) + try: + error_context.context("Read CPUID leaf %s in guest" % cpuid_leaf, test.log.info) + regs = _read_cpuid_leaf(session, cpuid_leaf) + test.log.info( + "Guest CPUID %s: eax=0x%x ebx=0x%x ecx=0x%x edx=0x%x", + cpuid_leaf, + regs["eax"], + regs["ebx"], + regs["ecx"], + regs["edx"], + ) + + failures = [] + for reg_name in ("eax", "ebx", "ecx", "edx"): + expected_key = "expected_%s" % reg_name + if params.get(expected_key) is not None: + expected = params.get_numeric(expected_key) + if regs[reg_name] != expected: + failures.append( + "%s: got 0x%x, expected 0x%x" + % (reg_name, regs[reg_name], expected) + ) + + if failures: + test.fail("CPUID %s mismatch:\n " % cpuid_leaf + "\n ".join(failures)) + test.log.info("CPUID %s check passed", cpuid_leaf) + finally: + session.close() diff --git a/KVM/qemu/tests/kvm_memory_hotplug.py b/KVM/qemu/tests/kvm_memory_hotplug.py new file mode 100644 index 00000000..d616932f --- /dev/null +++ b/KVM/qemu/tests/kvm_memory_hotplug.py @@ -0,0 +1,38 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +# Author: Kai Zhang +# +# History: Jun. 2026 - Kai Zhang - creation + +from provider import dmesg_router # pylint: disable=unused-import +from virttest import error_context, env_process +from virttest.utils_test.qemu import MemoryHotplugTest + + +@error_context.context_aware +def run(test, params, env): + """ + KVM memory hotplug & unplug test: + 1. boot legacy VM with memory hotplug capability enabled + 2. check whether hotplugged memory is increasing correctly + 3. hot unplug the hot plugged memory + + :param test: QEMU test object + :param params: Dictionary with the test parameters + :param env: Dictionary with test environment. + """ + + params["start_vm"] = "yes" + env_process.preprocess_vm(test, params, env, params["main_vm"]) + vm = env.get_vm(params["main_vm"]) + vm.verify_alive() + timeout = params.get_numeric("login_timeout", 240) + session = vm.wait_for_login(timeout=timeout) + mem_name = params["target_mems"] + hotplug_test = MemoryHotplugTest(test, params, env) + hotplug_test.hotplug_memory(vm, mem_name) + hotplug_test.unplug_memory(vm, mem_name) + session.close() diff --git a/KVM/qemu/tests/live_migration_repeat.py b/KVM/qemu/tests/live_migration_repeat.py new file mode 100644 index 00000000..435d236b --- /dev/null +++ b/KVM/qemu/tests/live_migration_repeat.py @@ -0,0 +1,54 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +import time + +from provider import dmesg_router # pylint: disable=unused-import + +from virttest import error_context + + +@error_context.context_aware +def run(test, params, env): + """ + Live migration repeat test: + 1) Boot a guest VM. + 2) Perform local live migration N times in a loop. + 3) After each migration, log in to verify guest is alive. + 4) Destroy VM. + + :param test: QEMU test object. + :param params: Dictionary with test parameters. + :param env: Dictionary with the test environment. + """ + vm = env.get_vm(params["main_vm"]) + vm.verify_alive() + + login_timeout = int(params.get("login_timeout", 360)) + migration_iterations = int(params.get("migration_iterations", 1)) + iteration_sleep_time = int(params.get("iteration_sleep_time", 5)) + mig_timeout = float(params.get("mig_timeout", 300)) + + error_context.context("Verify guest is bootable before migration", test.log.info) + session = vm.wait_for_login(timeout=login_timeout) + session.close() + + try: + for i in range(1, migration_iterations + 1): + error_context.context( + "Live migration iteration %d of %d" % (i, migration_iterations), + test.log.info, + ) + time.sleep(iteration_sleep_time) + vm.migrate(timeout=mig_timeout) + + error_context.context( + "Verify guest alive after migration %d" % i, test.log.info + ) + session = vm.wait_for_login(timeout=30) + session.close() + test.log.info("Migration iteration %d PASS", i) + finally: + vm.destroy(gracefully=False) diff --git a/KVM/qemu/tests/live_migration_verify.py b/KVM/qemu/tests/live_migration_verify.py new file mode 100644 index 00000000..2c6539ad --- /dev/null +++ b/KVM/qemu/tests/live_migration_verify.py @@ -0,0 +1,225 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +import time + +from provider import dmesg_router # pylint: disable=unused-import + +from virttest import error_context +from virttest import utils_package + + +@error_context.context_aware +def run(test, params, env): + """ + Live migration verification test: + 1) Boot guest and collect pre-migration state. + 2) Perform local live migration. + 3) Log in after migration and verify state is preserved. + + Supported verify_target values: + - cpu_flags: Verify CPU flags are preserved after migration. + - cpu_num: Verify CPU count matches after migration. + - mem_size: Verify memory size is preserved after migration. + - time: Verify time drift is within threshold after migration. + - continuity: Start a process before migration, verify it survives. + + :param test: QEMU test object. + :param params: Dictionary with test parameters. + :param env: Dictionary with the test environment. + """ + vm = env.get_vm(params["main_vm"]) + vm.verify_alive() + + login_timeout = int(params.get("login_timeout", 360)) + mig_timeout = float(params.get("mig_timeout", 300)) + verify_target = params["verify_target"] + + error_context.context("Log into guest before migration", test.log.info) + session = vm.wait_for_login(timeout=login_timeout) + + try: + # Collect pre-migration state + pre_state = _collect_state(test, session, params, verify_target) + + # For continuity test, start background process before migration + if verify_target == "continuity": + _start_continuity_process(test, session, params) + + session.close() + + # Perform migration + error_context.context("Perform local live migration", test.log.info) + vm.migrate(timeout=mig_timeout) + + # Log in after migration + error_context.context("Log into guest after migration", test.log.info) + session = vm.wait_for_login(timeout=30) + + # Verify post-migration state + error_context.context( + "Verify %s after migration" % verify_target, test.log.info + ) + _verify_state(test, session, params, verify_target, pre_state) + finally: + if session: + session.close() + vm.destroy(gracefully=False) + + +def _collect_state(test, session, params, verify_target): + """Collect guest state before migration for comparison.""" + state = {} + if verify_target == "cpu_flags": + output = session.cmd_output("cat /proc/cpuinfo | grep flags | head -1") + flags = set(output.strip().split(":")[1].split()) if ":" in output else set() + state["cpu_flags"] = flags + test.log.info("Pre-migration CPU flags count: %d", len(flags)) + + elif verify_target == "cpuid": + cpuid_cmd = params.get("cpuid_dump_cmd", "cpuid -1 -r") + if session.cmd_status("which cpuid") != 0: + if not utils_package.package_install("cpuid", session): + test.cancel("cpuid package is not available in guest") + output = session.cmd_output(cpuid_cmd, timeout=60) + state["cpuid"] = output.strip() + test.log.info( + "Pre-migration CPUID dump collected (%d lines)", + len(output.strip().splitlines()), + ) + + elif verify_target == "cpu_num": + output = session.cmd_output("nproc").strip() + state["cpu_num"] = int(output) + test.log.info("Pre-migration CPU count: %s", output) + + elif verify_target == "mem_size": + output = session.cmd_output( + "grep MemTotal /proc/meminfo | awk '{print $2}'" + ).strip() + state["mem_kb"] = int(output) + test.log.info("Pre-migration MemTotal: %s kB", output) + + elif verify_target == "time": + host_time = time.time() + guest_time_str = session.cmd_output("date +%s").strip() + state["host_time"] = host_time + state["guest_time"] = float(guest_time_str) + test.log.info( + "Pre-migration host_time=%.2f guest_time=%.2f", + host_time, + state["guest_time"], + ) + + return state + + +def _start_continuity_process(test, session, params): + """Start a background process for continuity verification.""" + continuity_cmd = params.get("continuity_cmd", "cat /dev/urandom > /dev/null") + test.log.info("Starting continuity process: %s", continuity_cmd) + session.sendline("nohup %s &" % continuity_cmd) + time.sleep(3) + + # Verify process started + check_cmd = params.get("continuity_check_cmd", 'pgrep -f "cat /dev/urandom"') + status = session.cmd_status(check_cmd) + if status != 0: + test.error("Continuity process did not start") + + +def _verify_state(test, session, params, verify_target, pre_state): + """Verify guest state after migration matches pre-migration state.""" + if verify_target == "cpu_flags": + output = session.cmd_output("cat /proc/cpuinfo | grep flags | head -1") + post_flags = ( + set(output.strip().split(":")[1].split()) if ":" in output else set() + ) + missing = pre_state["cpu_flags"] - post_flags + extra = post_flags - pre_state["cpu_flags"] + if missing: + test.fail("CPU flags lost after migration: %s" % " ".join(missing)) + if extra: + test.log.warning("Extra CPU flags after migration: %s", " ".join(extra)) + test.log.info("CPU flags preserved after migration (%d flags)", len(post_flags)) + + elif verify_target == "cpuid": + cpuid_cmd = params.get("cpuid_dump_cmd", "cpuid -1 -r") + post_output = session.cmd_output(cpuid_cmd, timeout=60).strip() + pre_output = pre_state["cpuid"] + if pre_output != post_output: + pre_lines = pre_output.splitlines() + post_lines = post_output.splitlines() + diffs = [] + for i, (pre_l, post_l) in enumerate(zip(pre_lines, post_lines)): + if pre_l != post_l: + diffs.append("line %d: before=%r after=%r" % (i + 1, pre_l, post_l)) + if len(pre_lines) != len(post_lines): + diffs.append( + "line count: before=%d after=%d" % (len(pre_lines), len(post_lines)) + ) + test.fail("CPUID changed after migration:\n%s" % "\n".join(diffs[:20])) + test.log.info( + "CPUID preserved after migration (%d lines)", + len(post_output.splitlines()), + ) + + elif verify_target == "cpu_num": + output = session.cmd_output("nproc").strip() + post_num = int(output) + if post_num != pre_state["cpu_num"]: + test.fail( + "CPU count changed after migration: " + "before=%d after=%d" % (pre_state["cpu_num"], post_num) + ) + test.log.info("CPU count preserved after migration: %d", post_num) + + elif verify_target == "mem_size": + output = session.cmd_output( + "grep MemTotal /proc/meminfo | awk '{print $2}'" + ).strip() + post_mem = int(output) + # Allow 1% tolerance for memory size + diff_pct = abs(post_mem - pre_state["mem_kb"]) / pre_state["mem_kb"] * 100 + if diff_pct > 1: + test.fail( + "Memory size changed after migration: " + "before=%d kB after=%d kB (diff=%.2f%%)" + % (pre_state["mem_kb"], post_mem, diff_pct) + ) + test.log.info("Memory size preserved after migration: %d kB", post_mem) + + elif verify_target == "time": + host_time_after = time.time() + guest_time_str = session.cmd_output("date +%s").strip() + guest_time_after = float(guest_time_str) + + host_elapsed = host_time_after - pre_state["host_time"] + guest_elapsed = guest_time_after - pre_state["guest_time"] + drift = abs(host_elapsed - guest_elapsed) + drift_threshold = float(params.get("drift_threshold", 10)) + + test.log.info( + "Host elapsed: %.2f s, Guest elapsed: %.2f s, " + "Drift: %.2f s (threshold: %.2f s)", + host_elapsed, + guest_elapsed, + drift, + drift_threshold, + ) + if drift > drift_threshold: + test.fail( + "Time drift too large after migration: " + "%.2f seconds (threshold: %.2f)" % (drift, drift_threshold) + ) + + elif verify_target == "continuity": + check_cmd = params.get("continuity_check_cmd", 'pgrep -f "cat /dev/urandom"') + status = session.cmd_status(check_cmd) + if status != 0: + test.fail("Continuity process not running after migration") + test.log.info("Continuity process survived migration") + # Clean up + session.cmd_status('pkill -f "cat /dev/urandom"') diff --git a/KVM/qemu/tests/mce_einj.py b/KVM/qemu/tests/mce_einj.py new file mode 100644 index 00000000..737ca464 --- /dev/null +++ b/KVM/qemu/tests/mce_einj.py @@ -0,0 +1,161 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2025 Intel Corporation + +# Author: Farrah Chen +# +# History: Nov. 2025 - Farrah Chen - creation + +from provider import dmesg_router # pylint: disable=unused-import +from provider import lmce_utils +import logging +import os +import re +from avocado.utils import process +from avocado.core import exceptions +from virttest import error_context, env_process +from virttest import utils_misc + + +def error_inject(test, params, addr): + """ + Check if kernel module einj is loaded, if not, load it. + Inject error via einj + :param test: QEMU test object + :param params: Dictionary with the test parameters + :param addr: Host physical address + """ + module = 'einj' + if module not in process.system_output('lsmod').decode('utf-8'): + if process.system('modprobe %s' % module, shell=True) != 0: + test.cancel("module %s isn't supported ?" % module) + debugfs = '/sys/kernel/debug' + einj_path = os.path.join(debugfs, 'apei/einj/') + if not os.path.exists(einj_path): + test.cancel("error injection isn't supported, check your BIOS setting") + error_type = params.get('error_type') + status = process.system("echo %s > %s/error_type" % (error_type, einj_path), shell=True) + if status: + raise exceptions.TestError("Failed to inject error %s" % error_type) + status = process.system("echo %s > %s/param1" % (addr, einj_path), shell=True) + if status: + raise exceptions.TestError("Failed to inject error to address %s" % addr) + status = process.system("echo 0xfffffffffffff000 > %s/param2" % einj_path, shell=True) + if status: + raise exceptions.TestError("Failed to inject mask to param2") + status = process.system("echo 1 > %s/notrigger" % einj_path, shell=True) + if status: + raise exceptions.TestError("Failed to enable notrigger") + status = process.system("echo 1 > %s/error_inject" % einj_path, shell=True) + if status: + raise exceptions.TestError("Failed to inject error") + + +@error_context.context_aware +def run(test, params, env): + """ + Inject error to guest memory. + 0) Before executing this case, enable error injection, disable Patrol Scrub in BIOS + 1) Boot up guest + 2) Run victim in guest to get a physical address in guest + 3) Run gpa2hpa in QEMU monitor to get it's host physical address + 4) Return to host, inject error to this address by einj + 5) Return to guest victim, "enter" to trigger error + 6) Shutdown guest + :param test: QEMU test object + :param params: Dictionary with the test parameters + :param env: Dictionary with test environment. + """ + # Broadcast SRAO risk (mce_einj.srao_lmce_off only; mce_einj.srao_lmce_on / + # mce_einj.srar_lmce_on deliver locally to a single vCPU and are not + # affected): + # + # srao_lmce_off runs with LMCE off, so the SRAO delivered through the + # host EINJ -> KVM path is broadcast to every guest vCPU. The guest + # kernel then enters the mce_start rendezvous with a fixed + # MCE_TIMEOUT_US window; if any vCPU misses that window the guest + # panics in mce_panic() before it can log the SIGBUS, and this case + # fails. It is a probabilistic host + KVM + guest timing issue, not a + # defect of the case. When mce_einj.srao_lmce_off fails intermittently, + # rerun before treating it as a real regression. + lmce_state = params.get("lmce_state") + session = None + vm_exec_bin = None + test_dir = params["test_dir"] + try: + vm_name = params['main_vm'] + env_process.preprocess_vm(test, params, env, vm_name) + vm = env.get_vm(vm_name) + session = vm.wait_for_login() + vm_exec_bin = lmce_utils.build_victim(test, params, vm, session) + vmpha_log = '/tmp/vmpha.log' + session.cmd('%s -d -k 0 > %s 2>&1 &' % (vm_exec_bin, vmpha_log)) + # victim runs in the background and prints the GPA a moment later; + # poll the log until the address line appears. + guest_pha_int = utils_misc.wait_for( + lambda: lmce_utils.parse_victim_gpa(session, vmpha_log), + timeout=30, first=1, step=1) + if guest_pha_int is None: + raise exceptions.TestError("Victim did not report a GPA.") + guest_pha = '0x%x' % guest_pha_int + output = vm.monitor.send_args_cmd("gpa2hpa %s" % guest_pha) + host_pha = output.split()[7] + error_inject(test, params, host_pha) + vm_trigger_cmd = 'echo "trigger" > %s/trigger_start' % test_dir + session.cmd(vm_trigger_cmd) + hw_mce = 'err_code:0x00a0:0x0090 SystemAddress:0x%s' % host_pha.lstrip('0x') + vm_mce = 'mce: Uncorrected hardware memory error in user-access at %s' % guest_pha.lstrip('0x') + # The poisoned page is consumed asynchronously: the guest victim's + # poll loop wakes up on the trigger file, reads the page, the memory + # controller raises the MCE, KVM forwards it, then the host EDAC path + # decodes and logs it. Poll both dmesg streams instead of racing them. + hw_status = utils_misc.wait_for( + lambda: re.search(hw_mce, process.system_output('dmesg').decode('utf-8')), + timeout=30, first=1, step=1) + if not hw_status: + raise exceptions.TestError("Failed to trigger MCE in host") + vm_status = utils_misc.wait_for( + lambda: re.search(vm_mce, session.cmd_output('dmesg')), + timeout=30, first=1, step=1) + if not vm_status: + raise exceptions.TestError("Failed to trigger MCE in guest") + vm_dmesg = session.cmd_output('dmesg') + vm.verify_dmesg() + + # LMCE variants additionally verify that KVM forwarded the MCE with + # MCG_STATUS.LMCE_S set: guest MSR bits reflect lmce=on and the guest + # kernel logs an LMCE marker. This proves the local-delivery path + # (vs broadcast) survives the host EINJ -> KVM -> guest chain. + if lmce_state: + lmce_utils.install_msr_tools(test, session) + lmce_utils.check_lmce_state(test, session, lmce_state) + lmce_utils.check_lmce_marker(test, vm_dmesg, lmce_state) + + except Exception: + # Only the broadcast SRAO path is exposed to the rendezvous panic; + # emit the retry hint only for that variant so lmce=on failures are + # still surfaced as real bugs. + if not lmce_state: + logging.warning( + "mce_einj.srao_lmce_off failed. Broadcast SRAO can panic " + "the guest via the mce_start rendezvous timeout on the " + "host EINJ -> KVM -> guest path (see the comment at the top " + "of run()). This is a probabilistic timing issue, not a " + "defect of the case; please rerun before treating it as a " + "regression." + ) + raise + finally: + # Guard against the guest being unreachable after a rendezvous + # panic: any of these cleanup commands may hang or fail, and we + # must not mask the original exception with a cleanup NameError. + if session is not None: + try: + session.cmd("rm -rf /tmp/vmpha.log") + session.cmd("rm -rf %s/trigger_start" % test_dir) + if vm_exec_bin: + session.cmd("rm -rf %s" % vm_exec_bin) + except Exception: + pass + session.close() diff --git a/KVM/qemu/tests/mce_hmp.py b/KVM/qemu/tests/mce_hmp.py new file mode 100644 index 00000000..b6b24dfa --- /dev/null +++ b/KVM/qemu/tests/mce_hmp.py @@ -0,0 +1,258 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +# Author: Farrah Chen +# +# History: Jul. 2026 - Farrah Chen - creation + +from provider import dmesg_router # pylint: disable=unused-import +from provider import lmce_utils + +from virttest import env_process, error_context, utils_misc + + +def _inject_mce( + vm, + cpu, + bank, + status, + addr, + misc=0x8C, + mcg_status=lmce_utils.MCG_STATUS_MCIP_RIPV, + broadcast=False, +): + """Inject an MCE into the guest via QEMU HMP and return the QMP event. + + QEMU raises a ``MEMORY_FAILURE`` QMP event once it has delivered the + exception to the guest; that event is a reliable, guest-independent + proof of injection and is used in place of legacy mce-inject/mcelog + scraping. The event may also be picked up by the caller via + ``vm.monitor.get_event("MEMORY_FAILURE")`` after this returns. + """ + vm.monitor.clear_events() + prefix = "mce -b" if broadcast else "mce" + cmd = "%s %d %d 0x%x 0x%x 0x%x 0x%x" % ( + prefix, + cpu, + bank, + status, + mcg_status, + addr, + misc, + ) + vm.monitor.human_monitor_cmd(cmd) + return utils_misc.wait_for( + lambda: vm.monitor.get_event("MEMORY_FAILURE"), + timeout=10, + first=0.5, + step=0.5, + ) + + +def _get_guest_mce_dmesg(session): + """Return concatenated MCE-related dmesg lines from the guest.""" + return session.cmd_output( + "dmesg | grep -iE 'mce:|machine check|hardware error' || true" + ) + + +def _wait_for_mce_in_guest(session, timeout=30): + """Poll guest dmesg for an MCE record and return the matching lines. + + Returns the stripped dmesg output on the first successful poll, or ``""`` + on timeout. Callers can use the return value directly and avoid a second + dmesg query. + """ + + def _fetch(): + return _get_guest_mce_dmesg(session).strip() or None + + return utils_misc.wait_for(_fetch, timeout, first=1, step=1) or "" + + +def _launch_victim(session, params, log_path, exit_path, cpu=0): + """Launch victim pinned to ``cpu`` in auto-trigger mode, backgrounded. + + ``victim -d -k 0`` mmaps a page, prints its GPA, then polls + ``./trigger_start`` until it contains ``trigger`` and loops reading the + page. Its exit status is captured to ``exit_path`` so the caller can + verify SIGBUS delivery. + + Pinning is essential for SRAR injection: the MCE targets a specific + vCPU, and the kernel #MC handler on that vCPU delivers SIGBUS.AR to + the task currently running there. If the victim is running elsewhere, + the wrong task (typically the login shell) receives the fault. + """ + test_dir = params["test_dir"] + exe = params["exec_file"] + session.cmd( + "cd %s && (taskset -c %d ./%s -d -k 0 > %s 2>&1; echo $? > %s) &" + % (test_dir, cpu, exe, log_path, exit_path) + ) + + +def _run_srao(test, params, vm, session, expected_state): + """Inject an SRAO MCE and verify LMCE marker matches guest configuration. + + lmce=on -> local delivery with MCG_STATUS.LMCE_S set; guest dmesg must + contain an ``LMCE`` marker. lmce=off -> broadcast delivery; guest dmesg + must NOT contain an LMCE marker. + """ + lmce_utils.check_lmce_state(test, session, expected_state) + session.cmd("dmesg -C") + if expected_state == "on": + event = _inject_mce( + vm, + cpu=0, + bank=1, + status=lmce_utils.MCI_STATUS_SRAO, + addr=0x1000, + mcg_status=lmce_utils.MCG_STATUS_SRAO_LOCAL, + ) + else: + event = _inject_mce( + vm, + cpu=0, + bank=1, + status=lmce_utils.MCI_STATUS_SRAO, + addr=0x1000, + broadcast=True, + ) + if not event: + test.fail("QEMU did not report MEMORY_FAILURE for SRAO injection.") + test.log.info("SRAO delivered to guest: %s", event) + dmesg = _wait_for_mce_in_guest(session, timeout=10) + if not dmesg: + test.fail("SRAO not recorded in guest dmesg.") + test.log.info("SRAO recorded in guest dmesg: %s", dmesg) + lmce_utils.check_lmce_marker(test, dmesg, expected_state) + + +def _run_srar(test, params, vm, session, expected_state): + """Full SRAR test: inject on a mapped guest page, then trigger a user-space + load; expect the guest kernel to hwpoison the page and deliver SIGBUS.AR + to the victim process (exit status 135 = 128 + SIGBUS). + """ + lmce_utils.check_lmce_state(test, session, expected_state) + lmce_utils.build_victim(test, params, vm, session) + test_dir = params["test_dir"] + exe = params["exec_file"] + trigger = "%s/trigger_start" % test_dir + log = "%s/vmpha.log" % test_dir + exit_f = "%s/victim_exit" % test_dir + try: + session.cmd("rm -f %s %s %s" % (trigger, log, exit_f)) + session.cmd("dmesg -C") + target_cpu = 0 + _launch_victim(session, params, log, exit_f, cpu=target_cpu) + gpa = utils_misc.wait_for( + lambda: lmce_utils.parse_victim_gpa(session, log), + timeout=30, + first=1, + step=1, + ) + if not gpa: + test.error( + "Victim did not report a GPA (log: %s)." + % session.cmd_output("cat %s || true" % log) + ) + test.log.info( + "Victim mapped page at GPA 0x%x, pinned to vCPU %d.", gpa, target_cpu + ) + + # Trigger victim into the hot read loop BEFORE injecting so that the + # SRAR MCE (delivered with MCG_STATUS.EIPV=1) arrives while victim is + # the current task on the target vCPU. Otherwise SIGBUS.AR would be + # steered to whichever task happens to be scheduled on that vCPU. + session.cmd("echo trigger > %s" % trigger) + session.cmd_status("sleep 1") + + event = _inject_mce( + vm, + cpu=target_cpu, + bank=1, + status=lmce_utils.MCI_STATUS_SRAR, + addr=gpa, + mcg_status=lmce_utils.MCG_STATUS_SRAR_LOCAL, + ) + if not event: + test.fail("QEMU did not report MEMORY_FAILURE for SRAR injection.") + if not event.get("data", {}).get("flags", {}).get("action-required"): + test.fail("SRAR delivered without action-required flag: %s" % event) + test.log.info("SRAR delivered to guest: %s", event) + + # Victim should be killed by SIGBUS(7) on the poisoned load -> exit 135. + if not utils_misc.wait_for( + lambda: session.cmd_status("test -e %s" % exit_f) == 0, + timeout=30, + first=1, + step=1, + ): + test.fail( + "Victim did not exit after trigger; SRAR was not delivered " + "to the consumer process." + ) + raw = session.cmd_output("cat %s" % exit_f).strip() + try: + status = int(raw) + except ValueError: + test.fail("Invalid victim exit status: %r" % raw) + if status != 128 + 7: + test.fail( + "Victim did not die with SIGBUS: exit=%d (expected 135)." % status + ) + test.log.info("Victim received SIGBUS.AR as expected (exit=%d).", status) + + dmesg = _wait_for_mce_in_guest(session, timeout=10) + if not dmesg: + test.fail("SRAR not recorded in guest dmesg.") + if "Uncorrected" not in dmesg: + test.fail("SRAR dmesg missing 'Uncorrected' marker: %s" % dmesg) + lmce_utils.check_lmce_marker(test, dmesg, expected_state) + test.log.info("SRAR recorded in guest dmesg: %s", dmesg) + finally: + session.cmd_status("pkill -9 -x %s 2>/dev/null" % exe) + session.cmd_status("rm -f %s %s %s" % (trigger, log, exit_f)) + + +@error_context.context_aware +def run(test, params, env): + """ + Local Machine Check Exception (LMCE) tests. + + ``lmce_action`` selects the flow: + - ``check``: verify that guest LMCE MSR bits reflect the configured + ``-cpu ...,lmce=on|off`` flag. + - ``srao``: verify LMCE state, then inject an SRAO MCE via QEMU HMP + ``mce`` command and confirm the guest records the machine-check event. + - ``srar``: verify LMCE state, allocate a target guest page, then inject + an SRAR MCE tied to that page and confirm the guest records it. + + :param test: QEMU test object + :param params: Dictionary with the test parameters + :param env: Dictionary with test environment. + """ + action = params["lmce_action"] + expected_state = params["lmce_state"] + login_timeout = params.get_numeric("login_timeout", 240) + + params["start_vm"] = "yes" + env_process.preprocess_vm(test, params, env, params["main_vm"]) + vm = env.get_vm(params["main_vm"]) + vm.verify_alive() + session = vm.wait_for_login(timeout=login_timeout) + + try: + lmce_utils.install_msr_tools(test, session) + if action == "check": + lmce_utils.check_lmce_state(test, session, expected_state) + elif action == "srao": + _run_srao(test, params, vm, session, expected_state) + elif action == "srar": + _run_srar(test, params, vm, session, expected_state) + else: + test.error("Unknown lmce_action: %s" % action) + finally: + session.close() diff --git a/KVM/qemu/tests/nested_boot.py b/KVM/qemu/tests/nested_boot.py new file mode 100644 index 00000000..9c471fc7 --- /dev/null +++ b/KVM/qemu/tests/nested_boot.py @@ -0,0 +1,324 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation +# +# Nested KVM boot validation. +# +# Boots an L2 guest inside an L1 KVM guest using a copied L2 image +# attached to L1 as a virtio-blk device (/dev/vdb). No NFS or +# initramfs is required. Both raw and qcow2 images are supported. +# +# Flow: +# 1. Resolve the L1 image path and verify it exists (cancel if not). +# Nested KVM is enabled on the host via the cfg pre_command before +# this test runs. +# 2. Host: copy L1 image to L2 image (preserving original format). +# Attach the L2 image as image2 (virtio-blk) via avocado-vt params +# so L1 sees it as /dev/vdb. +# 3. Boot L1. +# 4. In L1: install qemu-kvm + sshpass, launch nested QEMU booted +# from /dev/vdb with user-mode NAT and hostfwd :2222->:22. +# 5. L1 ssh to L2 (localhost:2222) using L1's root password. +# 6. Verify basic L2 responsiveness (uname). +# 7. Cleanup: shutdown L2, remove L2 image file. + +import logging +import os +import re +import time + +from avocado.utils import process +from virttest import data_dir +from virttest import error_context +from virttest import storage +from virttest import utils_package + +LOG = logging.getLogger("avocado.test." + __name__) + + +# ---------- Host-side helpers ---------- + +def _get_l1_image_path(test, params): + """Resolve the L1 image path and cancel the test if it is missing. + Nested KVM is enabled on the host via the cfg pre_command before + the test starts.""" + image_params = params.object_params("image1") + l1_image = storage.get_image_filename(image_params, + data_dir.get_data_dir()) + if not os.path.isfile(l1_image): + test.cancel("L1 image not found: %s" % l1_image) + return l1_image + + +def _detect_image_format(image_path): + """Return 'raw' or 'qcow2' by probing the file with `qemu-img info`.""" + r = process.run("qemu-img info %s" % image_path, + ignore_status=True, shell=True) + if r.exit_status == 0: + m = re.search(r"^file format:\s*(\S+)", + r.stdout_text, re.MULTILINE) + if m: + return m.group(1) + try: + with open(image_path, "rb") as f: + head = f.read(4) + if head == b"QFI\xfb": + return "qcow2" + except OSError: + pass + return "raw" + + +def _prepare_l2_image(params, l1_image_path): + """Copy L1 image to L2 in the same host directory, preserving format.""" + l1_dir = os.path.dirname(l1_image_path) + l1_fmt = _detect_image_format(l1_image_path) + if l1_fmt not in ("raw", "qcow2"): + raise RuntimeError( + "Unsupported L1 image format %r (only raw/qcow2 supported)" + % l1_fmt) + + ext = ".qcow2" if l1_fmt == "qcow2" else ".raw" + l2_base = params.get("l2_image_basename", "l2_guest") + l2_image_path = os.path.join(l1_dir, l2_base + ext) + + if os.path.exists(l2_image_path): + os.remove(l2_image_path) + + LOG.info("cp --reflink L1 %s -> L2: %s -> %s", + l1_fmt, l1_image_path, l2_image_path) + process.run( + "cp --reflink=auto -f %s %s" % (l1_image_path, l2_image_path), + shell=True, timeout=600 + ) + + return { + "l2_image_path": l2_image_path, + "l2_image_base": os.path.join(l1_dir, l2_base), + "l2_image_format": l1_fmt, + } + + +def _attach_l2_as_vdb(params, l2_image_base, l2_format): + """Mutate params so avocado-vt attaches L2 as image2 (virtio-blk).""" + imgs = params.get("images", "image1").split() + if "image2" not in imgs: + imgs.append("image2") + params["images"] = " ".join(imgs) + params["image_name_image2"] = l2_image_base + params["image_format_image2"] = l2_format + params["image_snapshot_image2"] = "no" + params["create_image_image2"] = "no" + params["force_create_image_image2"] = "no" + params["remove_image_image2"] = "no" + params["check_image_image2"] = "no" + params["drive_format_image2"] = "virtio" + + +def _cleanup_l2_image(state): + if not state: + return + l2 = state.get("l2_image_path") + if l2 and os.path.exists(l2): + try: + os.remove(l2) + LOG.info("Removed L2 image: %s", l2) + except OSError as e: + LOG.warning("Failed to remove L2 image %s: %s", l2, e) + + +# ---------- L1-side helpers ---------- + +def _l1_find_l2_disk(session): + r = session.cmd_output("ls /dev/vdb 2>/dev/null; true", timeout=15) + if "/dev/vdb" in r: + return "/dev/vdb" + r = session.cmd_output( + "ls /dev/vd? 2>/dev/null | grep -v '/dev/vda' | head -1", + timeout=15 + ) + dev = r.strip().splitlines()[-1] if r.strip() else "" + if not dev: + return None + return dev + + +def _l1_install_deps(session): + """Install qemu and sshpass in L1. Return False on failure.""" + if not (utils_package.package_install("qemu-kvm", session) or + utils_package.package_install("qemu-system-x86", session)): + return False + if not utils_package.package_install("sshpass", session): + return False + return True + + +def _l1_copy_host_qemu(vm, session, params): + """Copy host qemu binary and its data dir into L1.""" + host_qemu = vm.qemu_binary + l1_qemu_path = "/usr/local/bin/qemu-system-x86_64" + vm.copy_files_to(host_qemu, l1_qemu_path) + session.cmd("chmod +x %s" % l1_qemu_path, timeout=10) + + # Copy QEMU data directory (BIOS, firmware ROMs) so the binary + # can find bios-256k.bin and other required files inside L1. + r = process.run("%s -L help" % host_qemu, + ignore_status=True, shell=True, timeout=10) + for line in r.stdout_text.splitlines(): + path = line.strip() + if path and os.path.isdir(path) and "share" in path: + session.cmd("mkdir -p /usr/local/share", timeout=10) + session.cmd("rm -rf /usr/local/share/qemu", timeout=10) + vm.copy_files_to(path, "/usr/local/share/") + break + + +def _l1_verify_kvm(session, test): + """Ensure /dev/kvm is present in L1 (nested KVM is enabled).""" + if session.cmd_status("test -e /dev/kvm") != 0: + # Try loading module in case L1 didn't auto-load it. nested=1 is + # not needed here: L1 only runs L2, not a further L3. + session.cmd("modprobe kvm_intel 2>/dev/null; true", + timeout=60, ignore_all_errors=True) + if session.cmd_status("test -e /dev/kvm") != 0: + test.fail("/dev/kvm not present in L1 - nested KVM unavailable") + + +def _boot_l2(session, params, l2_block_dev, l2_fmt="raw"): + """Boot L2 QEMU (daemonized) inside L1 using a block device.""" + l2_mem = params.get("l2_mem", "2048") + l2_smp = params.get("l2_smp", "2") + l2_cpu_flags = params.get("l2_cpu_flags", "") + ssh_port = params.get_numeric("l2_ssh_port", 2222) + + cpu_opt = "host" + if l2_cpu_flags: + cpu_opt = "host,%s" % l2_cpu_flags + + qemu_bin = "/usr/local/bin/qemu-system-x86_64" + qemu_datadir = "/usr/local/share/qemu" + session.cmd("rm -f /tmp/l2_serial.log /tmp/l2.pid") + cmd = ( + "%s -L %s -accel kvm -cpu %s -m %s -smp %s " + "-drive file=%s,format=%s,if=virtio,cache=none,aio=native " + "-netdev user,id=n0,hostfwd=tcp::%s-:22 " + "-device virtio-net,netdev=n0 " + "-display none -monitor none " + "-serial file:/tmp/l2_serial.log " + "-daemonize -pidfile /tmp/l2.pid" + % (qemu_bin, qemu_datadir, cpu_opt, l2_mem, l2_smp, + l2_block_dev, l2_fmt, ssh_port) + ) + LOG.info("Starting L2 QEMU: %s", cmd) + session.cmd(cmd, timeout=60) + return ssh_port + + +def _ssh_l2(session, ssh_port, password, command, timeout=120): + """Run a command in L2 via SSH from L1. Return stdout.""" + escaped = command.replace("'", "'\"'\"'") + ssh_cmd = ( + "sshpass -p '%s' ssh -p %s " + "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null " + "-o ConnectTimeout=5 root@localhost '%s' 2>/dev/null" + % (password, ssh_port, escaped) + ) + return session.cmd_output(ssh_cmd, timeout=timeout) + + +def _wait_l2_ssh(session, ssh_port, password, timeout): + """Poll until L2 SSH is reachable or timeout expires.""" + end = time.time() + timeout + while time.time() < end: + r = _ssh_l2(session, ssh_port, password, + "echo L2_SSH_READY", timeout=15) + if "L2_SSH_READY" in r: + return True + time.sleep(5) + return False + + +def _shutdown_l2(session, ssh_port, password): + try: + _ssh_l2(session, ssh_port, password, + "nohup poweroff >/dev/null 2>&1 &", timeout=15) + except Exception as e: + LOG.warning("L2 graceful poweroff failed: %s", e) + session.cmd( + "sleep 5; " + "[ -f /tmp/l2.pid ] && kill -9 $(cat /tmp/l2.pid) 2>/dev/null; true", + timeout=30 + ) + + +# ---------- Entry point ---------- + +@error_context.context_aware +def run(test, params, env): + """Nested KVM boot test: boot an L2 guest inside L1 and verify it.""" + l2_state = None + session = None + l2_booted = False + ssh_port = params.get_numeric("l2_ssh_port", 2222) + password = params.get("password", "") + + try: + error_context.context("Prepare L2 raw image on host", LOG.info) + l1_image = _get_l1_image_path(test, params) + l2_state = _prepare_l2_image(params, l1_image) + + error_context.context("Attach L2 image as vdb to L1", LOG.info) + _attach_l2_as_vdb(params, l2_state["l2_image_base"], + l2_state["l2_image_format"]) + + error_context.context("Boot L1 guest (with vdb=L2 image)", LOG.info) + vm = env.get_vm(params["main_vm"]) + vm.create(params=params) + vm.verify_alive() + session = vm.wait_for_login(timeout=360) + + error_context.context("Verify nested KVM in L1", LOG.info) + _l1_verify_kvm(session, test) + + error_context.context("Install QEMU/sshpass in L1", LOG.info) + if not _l1_install_deps(session): + test.error("Cannot install required packages in L1 " + "(check L1 network / repos)") + + error_context.context("Copy host QEMU binary to L1", LOG.info) + _l1_copy_host_qemu(vm, session, params) + + error_context.context("Locate L2 passthrough disk in L1", LOG.info) + l2_dev = _l1_find_l2_disk(session) + if not l2_dev: + test.error("L2 disk not found in L1 (expected /dev/vdb or " + "another virtio-blk device besides /dev/vda)") + LOG.info("L2 block device inside L1: %s", l2_dev) + + error_context.context("Boot L2 QEMU inside L1", LOG.info) + _boot_l2(session, params, l2_dev, l2_state["l2_image_format"]) + l2_booted = True + + boot_timeout = params.get_numeric("l2_boot_timeout", 180) + error_context.context("Wait for L2 sshd (timeout %ds)" + % boot_timeout, LOG.info) + if not _wait_l2_ssh(session, ssh_port, password, boot_timeout): + l2_log = session.cmd_output("tail -80 /tmp/l2_serial.log " + "2>/dev/null", timeout=15) + LOG.info("L2 serial tail:\n%s", l2_log) + test.fail("L2 SSH did not become ready within %ds" % boot_timeout) + + error_context.context("Verify L2 responsiveness (uname)", LOG.info) + out = _ssh_l2(session, ssh_port, password, "uname -a") + LOG.info("L2 uname: %s", out.strip()) + if "Linux" not in out: + test.fail("L2 uname did not report a Linux kernel: %r" % out) + + finally: + if l2_booted and session is not None: + try: + _shutdown_l2(session, ssh_port, password) + except Exception as e: + LOG.warning("L2 shutdown error: %s", e) + _cleanup_l2_image(l2_state) diff --git a/KVM/qemu/tests/nested_l2_test.py b/KVM/qemu/tests/nested_l2_test.py new file mode 100644 index 00000000..69b29a45 --- /dev/null +++ b/KVM/qemu/tests/nested_l2_test.py @@ -0,0 +1,186 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation +# +# Nested L2 hardware feature validation (PKU, vPMU). +# +# Reuses nested_boot infrastructure to boot L2 inside L1, then runs +# a feature-specific test inside L2. See nested_boot.py for the +# shared nested KVM boot flow. + +import logging +import os + +from virttest import data_dir +from virttest import error_context + +import nested_boot + +LOG = logging.getLogger("avocado.test." + __name__) + + +# ---------- L1->L2 SCP helper ---------- + +def _scp_to_l2(session, ssh_port, password, src, dst, timeout=60): + """SCP a file from L1 into L2.""" + scp_cmd = ( + "sshpass -p '%s' scp -P %s " + "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null " + "-o ConnectTimeout=5 %s root@localhost:%s 2>/dev/null" + % (password, ssh_port, src, dst) + ) + session.cmd(scp_cmd, timeout=timeout) + + +# ---------- Feature tests inside L2 ---------- + +def _run_pku_test(test, session, vm, ssh_port, password): + error_context.context("Verify PKU flag in L2 /proc/cpuinfo", LOG.info) + r = nested_boot._ssh_l2(session, ssh_port, password, + "grep -q pku /proc/cpuinfo && echo PKU_OK") + if "PKU_OK" not in r: + test.fail("PKU flag not present in L2 /proc/cpuinfo") + + error_context.context("Copy pku_test.c into L2 and compile", LOG.info) + deps_dir = data_dir.get_deps_dir("nested_l2_test") + pku_src = os.path.join(deps_dir, "pku_test.c") + vm.copy_files_to(pku_src, "/tmp/pku_test.c") + _scp_to_l2(session, ssh_port, password, + "/tmp/pku_test.c", "/tmp/pku_test.c") + build = nested_boot._ssh_l2(session, ssh_port, password, + "cd /tmp && gcc -o pku_test pku_test.c 2>&1") + if "error" in build.lower(): + test.fail("Failed to compile pku_test in L2: %s" % build) + + error_context.context("Run PKU selftest in L2", LOG.info) + out = nested_boot._ssh_l2(session, ssh_port, password, + "/tmp/pku_test 2>&1") + LOG.info("L2 PKU test output:\n%s", out) + if "all tests OK" not in out: + test.fail("PKU selftest did not report success in L2") + + +def _run_vpmu_test(test, session, ssh_port, password): + error_context.context("Install perf in L2", LOG.info) + install = nested_boot._ssh_l2( + session, ssh_port, password, + "which perf >/dev/null 2>&1 || " + "dnf install -y perf >/dev/null 2>&1 || " + "apt-get install -y linux-tools-common linux-tools-generic " + ">/dev/null 2>&1; which perf", + timeout=300, + ) + if "/perf" not in install: + test.fail("Could not install/find perf in L2") + + events = [ + "cpu-cycles", + "instructions", + "ref-cycles", + "branch-instructions", + "branch-misses", + ] + for event in events: + error_context.context("Run perf stat -e %s in L2" % event, + LOG.info) + out = nested_boot._ssh_l2( + session, ssh_port, password, + "perf stat -e %s -- sleep 1 2>&1" % event, + timeout=60, + ) + LOG.info("L2 perf [%s] output:\n%s", event, out) + if "" in out: + test.fail("PMU event %s reported in L2" % event) + # Verify counter is non-zero + found = False + for line in out.splitlines(): + parts = line.strip().split() + if len(parts) >= 2 and parts[1] == event: + try: + if int(parts[0].replace(",", "")) > 0: + found = True + break + except ValueError: + continue + if not found: + test.fail("PMU event %s returned zero or unparseable in L2: %s" + % (event, out)) + + +# ---------- Entry point ---------- + +@error_context.context_aware +def run(test, params, env): + """Nested L2 feature test (PKU or vPMU) using L2 image as L1's vdb.""" + l2_state = None + session = None + l2_booted = False + ssh_port = params.get_numeric("l2_ssh_port", 2222) + password = params.get("password", "") + + try: + error_context.context("Prepare L2 image on host", LOG.info) + l1_image = nested_boot._get_l1_image_path(test, params) + l2_state = nested_boot._prepare_l2_image(params, l1_image) + + error_context.context("Attach L2 image as vdb to L1", LOG.info) + nested_boot._attach_l2_as_vdb(params, l2_state["l2_image_base"], + l2_state["l2_image_format"]) + + error_context.context("Boot L1 guest (with vdb=L2 image)", LOG.info) + vm = env.get_vm(params["main_vm"]) + vm.create(params=params) + vm.verify_alive() + session = vm.wait_for_login(timeout=360) + + error_context.context("Verify nested KVM in L1", LOG.info) + nested_boot._l1_verify_kvm(session, test) + + error_context.context("Install QEMU/sshpass in L1", LOG.info) + if not nested_boot._l1_install_deps(session): + test.error("Cannot install required packages in L1 " + "(check L1 network / repos)") + + error_context.context("Copy host QEMU binary to L1", LOG.info) + nested_boot._l1_copy_host_qemu(vm, session, params) + + error_context.context("Locate L2 passthrough disk in L1", LOG.info) + l2_dev = nested_boot._l1_find_l2_disk(session) + if not l2_dev: + test.error("L2 disk not found in L1 (expected /dev/vdb or " + "another virtio-blk device besides /dev/vda)") + LOG.info("L2 block device inside L1: %s", l2_dev) + + error_context.context("Boot L2 QEMU inside L1", LOG.info) + nested_boot._boot_l2(session, params, l2_dev, + l2_state["l2_image_format"]) + l2_booted = True + + boot_timeout = params.get_numeric("l2_boot_timeout", 180) + error_context.context("Wait for L2 sshd (timeout %ds)" + % boot_timeout, LOG.info) + if not nested_boot._wait_l2_ssh(session, ssh_port, password, + boot_timeout): + l2_log = session.cmd_output("tail -80 /tmp/l2_serial.log " + "2>/dev/null", timeout=15) + LOG.info("L2 serial tail:\n%s", l2_log) + test.fail("L2 SSH did not become ready within %ds" % boot_timeout) + + l2_test_type = params.get("l2_test_type") + error_context.context("Run L2 feature test: %s" % l2_test_type, + LOG.info) + if l2_test_type == "pku": + _run_pku_test(test, session, vm, ssh_port, password) + elif l2_test_type == "vpmu": + _run_vpmu_test(test, session, ssh_port, password) + else: + test.error("Unknown l2_test_type: %s" % l2_test_type) + + finally: + if l2_booted and session is not None: + try: + nested_boot._shutdown_l2(session, ssh_port, password) + except Exception as e: + LOG.warning("L2 shutdown error: %s", e) + nested_boot._cleanup_l2_image(l2_state) diff --git a/KVM/qemu/tests/nmis_nonmis.py b/KVM/qemu/tests/nmis_nonmis.py new file mode 100644 index 00000000..cfc4f7b2 --- /dev/null +++ b/KVM/qemu/tests/nmis_nonmis.py @@ -0,0 +1,57 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +from provider import dmesg_router # pylint: disable=unused-import +from provider.cpuid_utils import check_cpuid, prepare_cpuid +from provider.test_utils import get_baremetal_dir +from virttest import env_process, error_context + + +@error_context.context_aware +def run(test, params, env): + """ + Verify NMI source CPUID bit is hidden when -nmi-source is set. + + NMI Source depends on FRED. The test first confirms FRED CPUID is + present in the guest, then verifies that the NMI Source bit is NOT + visible when QEMU disables it via -nmi-source. + + :param test: QEMU test object + :param params: Dictionary with the test parameters + :param env: Dictionary with test environment + """ + fred_cpuid_arg = params["fred_cpuid"] + nmis_cpuid_arg = params["cpuid"] + test_dir = params["test_dir"] + bm_dir = get_baremetal_dir(params) + src_dir = "%s/tools/cpuid_check" % bm_dir + + error_context.context("Boot VM with -nmi-source disabled", test.log.info) + params["start_vm"] = "yes" + vm_name = params["main_vm"] + env_process.preprocess_vm(test, params, env, vm_name) + vm = env.get_vm(vm_name) + vm.verify_alive() + session = vm.wait_for_login() + try: + exec_bin = prepare_cpuid(test, params, src_dir, vm, session) + + error_context.context("Check FRED CPUID as prerequisite", test.log.info) + if check_cpuid(fred_cpuid_arg, exec_bin, session): + test.cancel("FRED CPUID not set, NMI source depends on FRED") + s, output = session.cmd_status_output("grep -w fred /proc/cpuinfo") + if s: + test.cancel("FRED cpu flag not found in guest /proc/cpuinfo") + + error_context.context( + "Verify NMI source CPUID bit is NOT set in guest", test.log.info + ) + if not check_cpuid(nmis_cpuid_arg, exec_bin, session): + test.fail("NMI source CPUID bit should NOT be set with -nmi-source") + test.log.info("NMI source CPUID correctly hidden with -nmi-source") + finally: + session.cmd("rm %s/cpuid* -rf" % test_dir, ignore_all_errors=True) + session.close() + vm.destroy(gracefully=False) diff --git a/KVM/qemu/tests/pf_intercepts_ept.py b/KVM/qemu/tests/pf_intercepts_ept.py new file mode 100644 index 00000000..46b271d7 --- /dev/null +++ b/KVM/qemu/tests/pf_intercepts_ept.py @@ -0,0 +1,121 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + + +import os +import time + +from provider import dmesg_router # pylint: disable=unused-import +from provider import kvm_trace_utils + +from avocado.utils import process + +from virttest import env_process, error_context + + +HOST_TRACE_FILE = kvm_trace_utils.HOST_TRACE_FILE + +# VMX EXIT_REASON_EXCEPTION_NMI = 0. Restricting the tracepoint to this exit +# reason keeps the ftrace buffer tiny even during a full guest boot. +KVM_EXIT_REASON_EXCEPTION_NMI = 0 + + +def _ensure_ept_enabled(test, params): + """Cancel unless the host reports Intel EPT enabled.""" + process.run("modprobe kvm", shell=True, ignore_status=True) + process.run("modprobe kvm_intel", shell=True, ignore_status=True) + ept_sysfs = params["ept_sysfs"] + if not os.path.exists(ept_sysfs): + test.cancel( + "Host does not expose %s; kvm_intel EPT status unknown" + % ept_sysfs + ) + with open(ept_sysfs) as fp: + ept = fp.read().strip() + if ept != "Y": + test.cancel( + "Host kvm_intel EPT not enabled (%s=%r)" % (ept_sysfs, ept) + ) + + +def _count_pf_intercepts(test, pf_vector): + """Count filtered EXCEPTION_NMI lines whose intr_info vector matches ``pf_vector``. + + The kvm_exit tracepoint's ``intr_info`` field is printed as ``0x%08x``; + the low 8 bits carry the interrupt vector on Intel VMX. We match on the + last two hex characters of the intr_info field to keep grep cheap. + """ + vec_hex = "%02x" % (pf_vector & 0xFF) + # grep -c returns 1 (no match) instead of failing the whole run. + pattern = "intr_info 0x[0-9a-fA-F]\\{6\\}%s" % vec_hex + result = process.run( + "grep -c -E %r %s || true" % (pattern, HOST_TRACE_FILE), + shell=True, ignore_status=True, + ) + return int(result.stdout_text.strip() or "0") + + +@error_context.context_aware +def run(test, params, env): + """ + Verify that host KVM does not intercept guest page faults when EPT is + enabled. + + Steps: + 1. Ensure ``kvm_intel`` is loaded with EPT enabled (skip otherwise). + 2. Enable the host ``kvm/kvm_exit`` tracepoint and clear its buffer. + 3. Boot the guest and let it run for ``trace_duration`` seconds so boot + and steady-state exits are captured. + 4. Read the ftrace buffer and count EXCEPTION_NMI exits whose + ``intr_info`` vector equals ``pf_vector`` (14 = #PF). + 5. Fail if any such intercept is observed; log the total EXCEPTION_NMI + count for context. + + :param test: QEMU test object + :param params: Dictionary with the test parameters + :param env: Dictionary with test environment. + """ + pf_vector = params.get_numeric("pf_vector", 14) + trace_duration = params.get_numeric("trace_duration", 10) + + _ensure_ept_enabled(test, params) + + error_context.context( + "Enable host kvm_exit tracepoint and clear buffer", + test.log.info, + ) + kvm_trace_utils.enable_kvm_exit_trace( + test, + filter_expr="exit_reason == %d" % KVM_EXIT_REASON_EXCEPTION_NMI, + ) + try: + error_context.context( + "Boot guest with EPT-backed memory", test.log.info, + ) + params["start_vm"] = "yes" + env_process.preprocess_vm(test, params, env, params["main_vm"]) + vm = env.get_vm(params["main_vm"]) + vm.verify_alive() + try: + time.sleep(trace_duration) + + error_context.context( + "Read host trace and count #PF intercepts", test.log.info, + ) + pf_count = _count_pf_intercepts(test, pf_vector) + test.log.info( + "#PF (vector %d) intercepts found: %d", + pf_vector, pf_count, + ) + if pf_count > 0: + test.fail( + "Detected %d guest #PF (vector %d) intercepts while EPT " + "is enabled; expected 0" + % (pf_count, pf_vector) + ) + finally: + vm.destroy(gracefully=False) + finally: + kvm_trace_utils.disable_kvm_exit_trace(clear_filter=True) diff --git a/KVM/qemu/tests/ras.py b/KVM/qemu/tests/ras.py deleted file mode 100644 index 5d4b95ae..00000000 --- a/KVM/qemu/tests/ras.py +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/python3 - -# SPDX-License-Identifier: GPL-2.0-only -# Copyright (c) 2025 Intel Corporation - -# Author: Farrah Chen -# -# History: Nov. 2025 - Farrah Chen - creation - -from provider import dmesg_router # pylint: disable=unused-import -import os -import re -from avocado.utils import process -from avocado.core import exceptions -from virttest import error_context, env_process -from virttest import utils_package -from virttest import data_dir as virttest_data_dir - - -def prepare_vm_victim(test, params, vm, session): - """ - Compile test tool victim in guest. - Return the execuable test tool with absolute path. - :param test: QEMU test object - :param params: Dictionary with the test parameters - :param vm: The vm object - :param session: Guest session - """ - source_file = params["source_file"] - exec_file = params["exec_file"] - test_dir = params["test_dir"] - deps_dir = virttest_data_dir.get_deps_dir('ras') - src_path = os.path.join(deps_dir, source_file) - vm.copy_files_to(src_path, test_dir) - if not utils_package.package_install("gcc", session): - test.cancel("Failed to install package gcc.") - compile_cmd = "cd %s && gcc %s -o %s" % (test_dir, source_file, exec_file) - status = session.cmd_status(compile_cmd) - if status: - raise exceptions.TestError("Victim compile failed.") - session.cmd("rm -rf %s/%s" % (test_dir, source_file)) - - return os.path.join(test_dir, exec_file) - - -def error_inject(test, params, addr): - """ - Check if kernel module einj is loaded, if not, load it. - Inject error via einj - :param test: QEMU test object - :param params: Dictionary with the test parameters - :param addr: Host physical address - """ - module = 'einj' - if module not in process.system_output('lsmod').decode('utf-8'): - if process.system('modprobe %s' % module, shell=True) != 0: - test.cancel("module %s isn't supported ?" % module) - debugfs = '/sys/kernel/debug' - einj_path = os.path.join(debugfs, 'apei/einj/') - if not os.path.exists(einj_path): - test.cancel("error injection isn't supported, check your BIOS setting") - error_type = params.get('error_type') - status = process.system("echo %s > %s/error_type" % (error_type, einj_path), shell=True) - if status: - raise exceptions.TestError("Failed to inject error %s" % error_type) - status = process.system("echo %s > %s/param1" % (addr, einj_path), shell=True) - if status: - raise exceptions.TestError("Failed to inject error to address %s" % addr) - status = process.system("echo 0xfffffffffffff000 > %s/param2" % einj_path, shell=True) - if status: - raise exceptions.TestError("Failed to inject mask to param2") - status = process.system("echo 1 > %s/notrigger" % einj_path, shell=True) - if status: - raise exceptions.TestError("Failed to enable notrigger") - status = process.system("echo 1 > %s/error_inject" % einj_path, shell=True) - if status: - raise exceptions.TestError("Failed to inject error") - - -@error_context.context_aware -def run(test, params, env): - """ - Inject error to guest memory. - 0) Before executing this case, enable error injection, disable Patrol Scrub in BIOS - 1) Boot up guest - 2) Run victim in guest to get a physical address in guest - 3) Run gpa2hpa in QEMU monitor to get it's host physical address - 4) Return to host, inject error to this address by einj - 5) Return to guest victim, "enter" to trigger error - 6) Shutdown guest - :param test: QEMU test object - :param params: Dictionary with the test parameters - :param env: Dictionary with test environment. - """ - try: - vm_name = params['main_vm'] - env_process.preprocess_vm(test, params, env, vm_name) - vm = env.get_vm(vm_name) - session = vm.wait_for_login() - vm_exec_bin = prepare_vm_victim(test, params, vm, session) - victim_cmd = '%s -d -k 0 > /tmp/vmpha.log &' % vm_exec_bin - session.cmd(victim_cmd) - vmpha_cmd = 'cat /tmp/vmpha.log' - output = session.cmd_output(vmpha_cmd) - guest_pha = output.split()[5] - output = vm.monitor.send_args_cmd("gpa2hpa %s" % guest_pha) - host_pha = output.split()[7] - error_inject(test, params, host_pha) - test_dir = params["test_dir"] - vm_trigger_cmd = 'echo "trigger" > %s/trigger_start' % test_dir - session.cmd(vm_trigger_cmd) - hw_mce = 'err_code:0x00a0:0x0090 SystemAddress:0x%s' % host_pha.lstrip('0x') - vm_mce = 'mce: Uncorrected hardware memory error in user-access at %s' % guest_pha.lstrip('0x') - hw_dmesg = process.system_output('dmesg').decode('utf-8') - vm_dmesg = session.cmd_output('dmesg') - hw_status = re.search('%s' % hw_mce, hw_dmesg) - if not hw_status: - raise exceptions.TestError("Failed to trigger MCE in host") - vm_status = re.search('%s' % vm_mce, vm_dmesg) - if not vm_status: - raise exceptions.TestError("Failed to trigger MCE in guest") - vm.verify_dmesg() - - finally: - session.cmd("rm -rf /tmp/vmpha.log") - session.cmd("rm -rf %s/trigger_start" % test_dir) - session.cmd("rm -rf %s" % vm_exec_bin) - session.close() diff --git a/KVM/qemu/tests/stress_test.py b/KVM/qemu/tests/stress_test.py new file mode 100644 index 00000000..6db67149 --- /dev/null +++ b/KVM/qemu/tests/stress_test.py @@ -0,0 +1,102 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +""" +CPU / memory / IO / disk stress workload inside a KVM guest. + +Boots one guest, installs the configured stress tool, runs it with the +configured worker/duration parameters, and verifies the tool's completion +banner reports "successful". +""" + +from provider import dmesg_router # pylint: disable=unused-import +from virttest import env_process +from virttest import error_context +from virttest import utils_package + + +def _run_stress(test, session, params): + """ + Run the configured stress tool inside the guest and return the tail of + its output log. + + :param test: QEMU test object + :param session: active guest login session + :param params: Cartesian params dict for the current case + :return: last line of the stress log captured in the guest + """ + tool = params["stress_tool"] + stress_cpu = params.get_numeric("stress_cpu") + stress_vm_workers = params.get_numeric("stress_vm_workers") + stress_vm_bytes = params["stress_vm_bytes"] + stress_io = params.get_numeric("stress_io") + stress_hdd = params.get_numeric("stress_hdd") + stress_hdd_bytes = params["stress_hdd_bytes"] + stress_timeout = params.get_numeric("stress_timeout") + + if not utils_package.package_install(tool, session): + test.cancel(f"Installation of {tool} failed; please install it manually.") + + guest_log = f"/tmp/{tool.replace('-', '_')}.log" + session.cmd(f"rm -f {guest_log}") + + # stress and stress-ng share the same option layout for these flags. + cmd = ( + f"{tool} --cpu {stress_cpu} --io {stress_io} " + f"--vm {stress_vm_workers} --vm-bytes {stress_vm_bytes} " + f"--hdd {stress_hdd} --hdd-bytes {stress_hdd_bytes} " + f"--timeout {stress_timeout} > {guest_log} 2>&1" + ) + # Allow the guest command up to timeout + 120s slack for setup / teardown. + session.cmd(cmd, timeout=stress_timeout + 120, ignore_all_errors=True) + + tail_line = session.cmd_output(f"tail -n 1 {guest_log}").strip() + session.cmd(f"rm -f {guest_log}") + return tail_line + + +@error_context.context_aware +def run(test, params, env): + """ + Run stress or stress-ng inside a guest and verify successful completion. + + Steps: + 1. Boot a single guest per case parameters. + 2. Install the configured stress tool inside the guest. + 3. Run the tool with configured worker / memory / disk / timeout + parameters. + 4. Verify the tool's completion line reports "successful". + + :param test: QEMU test object + :param params: Dictionary with the test parameters + :param env: Dictionary with test environment + """ + params["start_vm"] = "yes" + + vm_name = params["main_vm"] + env_process.preprocess_vm(test, params, env, vm_name) + vm = env.get_vm(vm_name) + vm.verify_alive() + + login_timeout = params.get_numeric("login_timeout", 240) + session = vm.wait_for_login(timeout=login_timeout) + try: + error_context.context( + f"Running {params['stress_tool']} in guest for " + f"{params.get_numeric('stress_timeout')}s", + test.log.info, + ) + tail_line = _run_stress(test, session, params) + test.log.info("Stress tool tail line: %s", tail_line) + + fields = tail_line.split() + status = fields[3] if len(fields) >= 4 else "" + if status != "successful": + test.fail( + f"{params['stress_tool']} did not report successful " + f"completion; last line: {tail_line!r}" + ) + finally: + session.close() diff --git a/KVM/qemu/tests/tdx_QGS_socket.py b/KVM/qemu/tests/tdx_QGS_socket.py new file mode 100644 index 00000000..3e896e37 --- /dev/null +++ b/KVM/qemu/tests/tdx_QGS_socket.py @@ -0,0 +1,41 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +# Author: Farrah Chen +# +# History: May. 2026 - Farrah Chen - creation + +from provider import dmesg_router # pylint: disable=unused-import +from virttest import error_context, env_process + + +@error_context.context_aware +def run(test, params, env): + """ + TDX QGS socket test: + 1. Boot TD VM with quote-generation-socket (vsock) configured + 2. Verify TD guest boots successfully + 3. Validate TSM report interface inside guest + + :param test: QEMU test object + :param params: Dictionary with the test parameters + :param env: Dictionary with test environment. + """ + error_context.context("Boot TD VM with QGS socket", test.log.info) + params["start_vm"] = "yes" + env_process.preprocess_vm(test, params, env, params["main_vm"]) + vm = env.get_vm(params["main_vm"]) + vm.verify_alive() + timeout = params.get_numeric("login_timeout", 240) + session = vm.wait_for_login(timeout=timeout) + + error_context.context("Validate TSM report interface in guest", test.log.info) + tsm_report_dir = params["tsm_report_dir"] + session.cmd("mkdir -p %s" % tsm_report_dir) + session.cmd("dd if=/dev/urandom bs=64 count=1 > %s/inblob" % tsm_report_dir) + session.cmd("hexdump -C %s/outblob" % tsm_report_dir, timeout=30, ok_status=[0]) + test.log.info("TSM report interface verified successfully") + + session.close() diff --git a/KVM/qemu/tests/tdx_hdevents_in5sec.py b/KVM/qemu/tests/tdx_hdevents_in5sec.py new file mode 100644 index 00000000..bffeb18a --- /dev/null +++ b/KVM/qemu/tests/tdx_hdevents_in5sec.py @@ -0,0 +1,65 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +# Author: Farrah Chen +# +# History: Jul. 2026 - Farrah Chen - creation + +from provider import dmesg_router # pylint: disable=unused-import + +from virttest import env_process, error_context, utils_package + + +@error_context.context_aware +def run(test, params, env): + """ + TDX PMU hardware events collection test: + 1. Host mediated-PMU setup (disable nmi_watchdog, reload kvm_intel with + enable_mediated_pmu=Y) is performed by avocado-vt via ``pre_command`` + before this handler runs. + 2. Boot TD VM with pmu=on. + 3. Run `perf stat` on six hardware events for a fixed duration in guest. + 4. Verify perf output contains the expected header line. + 5. Host restore (re-enable nmi_watchdog, reload kvm_intel without mediated + PMU) is performed by avocado-vt via ``post_command`` after this handler + returns and the VM is destroyed. + + :param test: QEMU test object + :param params: Dictionary with the test parameters + :param env: Dictionary with test environment. + """ + perf_events = params["perf_events"] + perf_duration = params.get_numeric("perf_duration", 5) + expected_output = params["perf_expected_output"] + login_timeout = params.get_numeric("login_timeout", 240) + + event_args = " ".join("-e %s" % ev.strip() for ev in perf_events.split(",")) + perf_cmd = "perf stat %s -a -- sleep %d 2>&1" % (event_args, perf_duration) + + error_context.context("Boot TD VM with pmu enabled", test.log.info) + params["start_vm"] = "yes" + env_process.preprocess_vm(test, params, env, params["main_vm"]) + vm = env.get_vm(params["main_vm"]) + vm.verify_alive() + session = vm.wait_for_login(timeout=login_timeout) + + error_context.context("Ensure perf tool is available in guest", + test.log.info) + if not utils_package.package_install("perf", session): + test.cancel("Failed to install perf in guest") + + error_context.context( + "Collect hardware events for %d seconds in guest" % perf_duration, + test.log.info, + ) + output = session.cmd_output(perf_cmd, timeout=perf_duration + 60) + test.log.info("perf output:\n%s", output) + if expected_output not in output: + test.fail( + "perf did not produce expected header %r; got: %s" + % (expected_output, output) + ) + + session.close() diff --git a/KVM/qemu/tests/tdx_ve_reduction.py b/KVM/qemu/tests/tdx_ve_reduction.py new file mode 100644 index 00000000..0a67bc4f --- /dev/null +++ b/KVM/qemu/tests/tdx_ve_reduction.py @@ -0,0 +1,184 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +import os + +from virttest import data_dir, env_process, error_context, utils_package +from virttest.utils_sys import check_dmesg_output + +from provider import dmesg_router # pylint: disable=unused-import +from provider.test_utils import get_baremetal_dir + + +def _prepare_guest_tdx_compliance_tree(test, params, vm, session, caselist_header): + """Copy tdx-compliance sources and case header into guest working directory. + + :param test: QEMU test object + :param params: Dictionary with the test parameters + :param vm: VM object used to copy files into guest + :param session: Guest session object + :param caselist_header: CPUID case header file name to copy + :return: Guest working directory path for tdx-compliance + """ + guest_dir = params["guest_test_dir"] + session.cmd("rm -rf %s && mkdir -p %s" % (guest_dir, guest_dir)) + + bm_dir = get_baremetal_dir(params) + src_subdir = params.get("tdx_compliance_src_subdir", "tdx-compliance") + src_dir = os.path.join(bm_dir, src_subdir) + + source_files = [ + "Makefile", + "tdx-compliance-main.c", + "tdx-compliance.h", + "tdx-compliance-msr.h", + "tdx-compliance-cr.h", + "tdcall.S", + "tdxcall.S", + ] + for source_file in source_files: + vm.copy_files_to(os.path.join(src_dir, source_file), guest_dir) + + deps_dir = data_dir.get_deps_dir(params["deps_subdir"]) + vm.copy_files_to( + os.path.join(deps_dir, caselist_header), + "%s/tdx-compliance-cpuid.h" % guest_dir, + ) + + return guest_dir + + +def _run_tdx_cpuid_compliance(test, session, guest_dir, cpu_pin=None, verify_ve=False): + """Build and execute one cpuid compliance run in guest and check results. + + :param test: QEMU test object + :param session: Guest session object + :param guest_dir: Guest directory containing tdx-compliance sources + :param cpu_pin: Optional host CPU index for numactl pinning + :param verify_ve: Whether to verify VE trigger count in guest dmesg + """ + ret = 0 + try: + status = session.cmd_status("cd %s && make clean && make" % guest_dir, timeout=120) + if status != 0: + make_output = session.cmd_output("cd %s && make 2>&1 | tail -50" % guest_dir) + test.fail( + "Failed to build tdx-compliance in guest; please check whether kernel-devel is installed. Build output:\n%s" + % make_output + ) + + try: + session.cmd("dmesg -C") + except Exception: + pass # dmesg clear may fail in some environments + + status = session.cmd_status("cd %s && insmod tdx-compliance.ko" % guest_dir) + if status != 0: + test.fail("Failed to load tdx-compliance.ko") + + status = session.cmd_status("echo kretprobe > /sys/kernel/debug/tdx/tdx-tests") + if status != 0: + test.fail("Failed to register kretprobe") + + if cpu_pin is not None: + if not utils_package.package_install("numactl", session): + test.cancel("Failed to install numactl for cpu pinned cpuid case") + trigger_cmd = ( + "numactl -C %s sh -c \"echo cpuid > /sys/kernel/debug/tdx/tdx-tests\"" + % cpu_pin + ) + else: + trigger_cmd = "echo cpuid > /sys/kernel/debug/tdx/tdx-tests" + + status = session.cmd_status(trigger_cmd) + if status != 0: + test.fail("Failed to trigger cpuid compliance run") + + result_output = session.cmd_output("cat /sys/kernel/debug/tdx/tdx-tests") + test.log.debug("tdx cpuid compliance raw output:\n%s", result_output) + if "FAIL:0" not in result_output: + test.fail("tdx compliance cpuid test failed:\n%s" % result_output) + test.log.info("tdx compliance cpuid test passed") + + if verify_ve: + dmesg_output = session.cmd_output("dmesg") + start_marker = "Testing CPUID start" + end_marker = "CPUID test end" + start_index = dmesg_output.find(start_marker) + end_index = dmesg_output.find(end_marker) + if start_index != -1 and end_index != -1 and end_index > start_index: + dmesg_scope = dmesg_output[start_index:end_index] + else: + dmesg_scope = dmesg_output + ve_trigger_count = dmesg_scope.count("VE trigger") + if ve_trigger_count != 1: + test.fail("Expected 1 VE trigger, got %s" % ve_trigger_count) + finally: + try: + session.cmd_status("echo unregister > /sys/kernel/debug/tdx/tdx-tests") + except Exception: + pass + try: + session.cmd_status("rmmod tdx_compliance") + except Exception: + pass + + +@error_context.context_aware +def run(test, params, env): + """TDX VE reduction cpuid and feature matrix test cases. + + :param test: QEMU test object + :param params: Dictionary with the test parameters + :param env: Dictionary with test environment + """ + case_action = params["case_action"] + timeout = params.get_numeric("login_timeout", 240) + vm_name = params["main_vm"] + + session = None + vm = None + try: + params["start_vm"] = "yes" + env_process.preprocess_vm(test, params, env, vm_name) + vm = env.get_vm(vm_name) + vm.verify_alive() + session = vm.wait_for_login(timeout=timeout) + + error_context.context("Install guest build prerequisites", test.log.info) + for pkg in ["gcc", "make"]: + if not utils_package.package_install(pkg, session): + test.cancel("Failed to install %s in guest" % pkg) + + if case_action == "ve_check": + error_context.context("Check guest dmesg for REDUCE_VE marker", test.log.info) + if not check_dmesg_output("REDUCE_VE", session=session): + test.fail("#VE Reduction is not enabled in TD guest") + test.log.info("tdx VE reduction check passed") + return + + if case_action == "compliance": + caselist = params["caselist"] + guest_dir = _prepare_guest_tdx_compliance_tree(test, params, vm, session, caselist) + cpu_pin = params.get("cpu_pin") + error_context.context("Run tdx cpuid compliance with %s" % caselist, test.log.info) + _run_tdx_cpuid_compliance(test, session, guest_dir, cpu_pin=cpu_pin, verify_ve=False) + return + + if case_action == "compliance_ve_series": + caselists = params.objects("ve_case_headers") + cpu_pin = params.get("cpu_pin") + for caselist in caselists: + guest_dir = _prepare_guest_tdx_compliance_tree(test, params, vm, session, caselist) + error_context.context("Run VE compliance caselist %s" % caselist, test.log.info) + _run_tdx_cpuid_compliance(test, session, guest_dir, cpu_pin=cpu_pin, verify_ve=True) + return + + test.error("Unsupported case_action: %s" % case_action) + finally: + if session: + session.close() + if vm and vm.is_alive(): + vm.destroy(gracefully=False) diff --git a/KVM/qemu/tests/tdx_without_nographic.py b/KVM/qemu/tests/tdx_without_nographic.py new file mode 100644 index 00000000..cf7e6739 --- /dev/null +++ b/KVM/qemu/tests/tdx_without_nographic.py @@ -0,0 +1,29 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +# Author: Kai Zhang +# +# History: May. 2026 - Kai Zhang - creation + +from provider import dmesg_router # pylint: disable=unused-import +from virttest import error_context, env_process + + +@error_context.context_aware +def run(test, params, env): + """ + Check whether TDX guest can boot up without nographic: + 1. Boot TDVM without -nographic parameter + + :param test: QEMU test object + :param params: Dictionary with the test parameters + :param env: Dictionary with test environment. + """ + + test.log.warning("Before running this test, please comment the `display` parameter in tdx_temp.cfg") + params["start_vm"] = "yes" + env_process.preprocess_vm(test, params, env, params["main_vm"]) + vm = env.get_vm(params["main_vm"]) + vm.verify_alive() diff --git a/KVM/qemu/tests/vcpu_boot.py b/KVM/qemu/tests/vcpu_boot.py new file mode 100644 index 00000000..6c054935 --- /dev/null +++ b/KVM/qemu/tests/vcpu_boot.py @@ -0,0 +1,56 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +from avocado.utils import cpu + +from virttest import env_process, error_context + +from provider import dmesg_router # pylint: disable=unused-import + + +@error_context.context_aware +def run(test, params, env): + """ + Boot a guest with vCPU count exceeding the host online CPU count. + + 1. Get host online CPU count + 2. Set guest vCPU count to host_count + 1 + 3. Boot guest with the computed vCPU count + 4. Verify guest reports expected vCPU count + + :param test: QEMU test object + :param params: Dictionary with the test parameters + :param env: Dictionary with test environment + """ + vm_name = params["main_vm"] + + error_context.context("Get host online CPU count", test.log.info) + host_cpu_count = cpu.online_count() + expected_vcpu = host_cpu_count + 1 + + error_context.context( + "Boot VM with %d vCPUs (host has %d)" % (expected_vcpu, host_cpu_count), + test.log.info, + ) + params["start_vm"] = "yes" + params["smp"] = str(expected_vcpu) + env_process.preprocess_vm(test, params, env, vm_name) + vm = env.get_vm(vm_name) + vm.verify_alive() + + timeout = params.get_numeric("login_timeout", 240) + session = vm.wait_for_login(timeout=timeout) + try: + error_context.context( + "Verify guest vCPU count is %d" % expected_vcpu, test.log.info + ) + actual_vcpu = vm.get_cpu_count() + if actual_vcpu != expected_vcpu: + test.fail( + "Guest vCPU count mismatch: expected %d, got %d" + % (expected_vcpu, actual_vcpu) + ) + finally: + session.close() diff --git a/KVM/qemu/tests/vcpu_hotplug.py b/KVM/qemu/tests/vcpu_hotplug.py new file mode 100644 index 00000000..905fe52e --- /dev/null +++ b/KVM/qemu/tests/vcpu_hotplug.py @@ -0,0 +1,140 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +# Author: Farrah Chen +# +# History: Jul. 2026 - Farrah Chen - creation + +from provider import dmesg_router # pylint: disable=unused-import + +from virttest import env_process, error_context, utils_misc + +from provider import cpu_utils + + +def _bring_online(session, cpu_ids): + """Online each guest CPU by id (idempotent).""" + for cid in cpu_ids: + session.cmd_status( + "echo 1 > /sys/devices/system/cpu/cpu%d/online" % cid + ) + + +def _bring_offline(session, cpu_ids): + """Offline each guest CPU by id (idempotent).""" + for cid in cpu_ids: + session.cmd_status( + "echo 0 > /sys/devices/system/cpu/cpu%d/online" % cid + ) + + +def _get_present_cpu_ids(session): + """Return the set of cpu ids present in guest (online or offline).""" + out = session.cmd_output("cat /sys/devices/system/cpu/present").strip() + ids = set() + for part in out.split(","): + part = part.strip() + if not part: + continue + if "-" in part: + a, b = part.split("-") + ids.update(range(int(a), int(b) + 1)) + else: + ids.add(int(part)) + return ids + + +def _hotplug_all(test, vm, session, vcpu_devices, initial_ids, verify_timeout, + label): + """Hotplug every configured vcpu device and verify guest CPU count.""" + for dev in vcpu_devices: + error_context.context("Hotplug %s (%s)" % (dev, label), test.log.info) + vm.hotplug_vcpu_device(dev) + + added_ids = sorted(_get_present_cpu_ids(session) - initial_ids) + _bring_online(session, added_ids) + + if not utils_misc.wait_for( + lambda: cpu_utils.check_if_vm_vcpus_match_qemu(vm), + verify_timeout, first=2, step=2, + ): + test.fail("Guest CPU count mismatch after hotplug (%s)" % label) + + +def _hotunplug_all(test, vm, session, vcpu_devices, initial_ids, + verify_timeout, label): + """Offline newly added CPUs, then hotunplug them and verify guest count.""" + added_ids = sorted(_get_present_cpu_ids(session) - initial_ids) + _bring_offline(session, added_ids) + + for dev in vcpu_devices: + error_context.context( + "Hotunplug %s (%s)" % (dev, label), test.log.info + ) + vm.hotunplug_vcpu_device(dev) + + if not utils_misc.wait_for( + lambda: cpu_utils.check_if_vm_vcpus_match_qemu(vm), + verify_timeout, first=2, step=2, + ): + test.fail("Guest CPU count mismatch after hotunplug (%s)" % label) + + +@error_context.context_aware +def run(test, params, env): + """ + vCPU hotplug tests using QEMU device_add / device_del. + + Actions (selected via ``vcpu_action``): + - ``hot_add``: hotplug every configured vcpu device and verify guest count. + - ``hot_remove``: hotplug + verify, then hotunplug + verify. + - ``repeat_hotplug``: repeat the hot_add + hot_remove cycle + ``vcpu_iterations`` times. + - ``multiple_hotplug``: hot_add + hot_remove with many devices in one cycle. + + :param test: QEMU test object + :param params: Dictionary with the test parameters + :param env: Dictionary with test environment. + """ + action = params["vcpu_action"] + iterations = params.get_numeric("vcpu_iterations", 1) + verify_timeout = params.get_numeric("verify_wait_timeout", 60) + login_timeout = params.get_numeric("login_timeout", 240) + smp = params.get_numeric("smp") + vcpu_devices = params.objects("vcpu_devices") + + params["start_vm"] = "yes" + env_process.preprocess_vm(test, params, env, params["main_vm"]) + vm = env.get_vm(params["main_vm"]) + vm.verify_alive() + session = vm.wait_for_login(timeout=login_timeout) + + try: + initial_ids = _get_present_cpu_ids(session) + if len(initial_ids) != smp: + test.error( + "Initial guest CPU count %d does not match smp=%d" + % (len(initial_ids), smp) + ) + + if action == "hot_add": + _hotplug_all(test, vm, session, vcpu_devices, initial_ids, + verify_timeout, "hot_add") + elif action in ("hot_remove", "multiple_hotplug"): + _hotplug_all(test, vm, session, vcpu_devices, initial_ids, + verify_timeout, action) + _hotunplug_all(test, vm, session, vcpu_devices, initial_ids, + verify_timeout, action) + elif action == "repeat_hotplug": + for i in range(iterations): + label = "iteration %d/%d" % (i + 1, iterations) + _hotplug_all(test, vm, session, vcpu_devices, initial_ids, + verify_timeout, label) + _hotunplug_all(test, vm, session, vcpu_devices, initial_ids, + verify_timeout, label) + else: + test.error("Unknown vcpu_action: %s" % action) + finally: + session.close() diff --git a/KVM/qemu/tests/virt_interrupt.py b/KVM/qemu/tests/virt_interrupt.py new file mode 100644 index 00000000..3ccaccc5 --- /dev/null +++ b/KVM/qemu/tests/virt_interrupt.py @@ -0,0 +1,85 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +""" +Verify virtual interrupt injection while a KVM guest is running. + +Boots a guest, records the kvm:kvm_inj_virq tracepoint on the host via +perf, then asserts that injected virtual interrupt count is greater than +zero. +""" + +from provider import dmesg_router # pylint: disable=unused-import +from avocado.utils import process +from virttest import env_process +from virttest import error_context + + +@error_context.context_aware +def run(test, params, env): + """ + Check virtual interrupt delivery while a guest is running. + + Steps: + 1. Start perf record for kvm:kvm_inj_virq on the host. + 2. Boot a guest. + 3. Wait for perf to finish recording. + 4. Parse perf report and verify kvm_inj_virq count > 0. + + :param test: QEMU test object + :param params: Dictionary with the test parameters + :param env: Dictionary with test environment + """ + perf_event = params["perf_event"] + perf_duration = params.get_numeric("perf_duration", 10) + login_timeout = params.get_numeric("login_timeout", 240) + + params["start_vm"] = "yes" + vm_name = params["main_vm"] + + error_context.context( + "Start perf record for %s on host" % perf_event, test.log.info + ) + perf_output = "/tmp/perf_virq.data" + perf_cmd = "perf record -e '%s' -a -o %s -- sleep %d" % ( + perf_event, + perf_output, + perf_duration, + ) + # Start perf in background; it will record for perf_duration seconds. + perf_proc = process.SubProcess(perf_cmd, shell=True) + perf_proc.start() + + try: + error_context.context("Boot guest", test.log.info) + env_process.preprocess_vm(test, params, env, vm_name) + vm = env.get_vm(vm_name) + vm.verify_alive() + session = vm.wait_for_login(timeout=login_timeout) + + error_context.context("Wait for perf recording to complete", test.log.info) + perf_proc.wait() + + error_context.context("Parse perf report for %s" % perf_event, test.log.info) + report = process.run( + "perf report -i %s --stdio 2>/dev/null | grep kvm_inj_virq" % perf_output, + shell=True, + ignore_status=True, + ) + test.log.info("perf report output: %s", report.stdout_text.strip()) + + # Extract the sample percentage or count line + lines = [ + l for l in report.stdout_text.strip().splitlines() if "kvm_inj_virq" in l + ] + if not lines: + test.fail( + "No kvm_inj_virq events recorded during guest boot; " + "virtual interrupt injection may not be working" + ) + test.log.info("Virtual interrupt injection verified: %s", lines[0].strip()) + finally: + session.close() + process.run("rm -f %s" % perf_output, ignore_status=True) diff --git a/KVM/qemu/tests/vm_save_restore.py b/KVM/qemu/tests/vm_save_restore.py new file mode 100644 index 00000000..77f7a88e --- /dev/null +++ b/KVM/qemu/tests/vm_save_restore.py @@ -0,0 +1,176 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +import os +import time + +from provider import dmesg_router # pylint: disable=unused-import + +from avocado.utils import process +from virttest import env_process +from virttest import error_context + + +@error_context.context_aware +def run(test, params, env): + """ + VM save/restore test. + + 1) Boot guest + 2) (Optional) Collect pre-SR baseline info + 3) Pause VM, save state to file + 4) Restore VM from file, resume + 5) Verify guest is alive (login) + 6) (Optional) Verify post-SR checks match baseline + 7) Repeat for sr_iterations + 8) Destroy guest + + :param test: QEMU test object + :param params: Dictionary with the test parameters + :param env: Dictionary with test environment + """ + sr_iterations = params.get_numeric("sr_iterations", 1) + sr_check = params.get("sr_check", "none") + sr_timeout = params.get_numeric("sr_timeout", 600) + login_timeout = params.get_numeric("login_timeout", 240) + save_path = os.path.join(test.tmpdir, "vm_state") + + error_context.context("Boot guest", test.log.info) + params["start_vm"] = "yes" + vm_name = params["main_vm"] + env_process.preprocess_vm(test, params, env, vm_name) + vm = env.get_vm(vm_name) + vm.verify_alive() + session = vm.wait_for_login(timeout=login_timeout) + + try: + # Collect pre-SR baseline if needed + baseline = _collect_baseline(test, session, params, sr_check) + + for i in range(1, sr_iterations + 1): + error_context.context("Save/Restore iteration %d/%d" % + (i, sr_iterations), test.log.info) + + # Pre-save setup for continuity check + if sr_check == "continuity": + _start_background_process(session, test) + + error_context.context("Pause and save VM to file", test.log.info) + vm.pause() + vm.save_to_file(save_path) + + error_context.context("Restore VM from file", test.log.info) + vm.restore_from_file(save_path) + vm.resume() + session = vm.wait_for_login(timeout=login_timeout) + test.log.info("Iteration %d: VM restored and login successful.", i) + + # Post-SR verification + if sr_check != "none": + error_context.context("Post-SR verification: %s" % sr_check, + test.log.info) + _verify_after_restore(test, session, params, sr_check, baseline) + + finally: + if os.path.exists(save_path): + os.remove(save_path) + try: + session.close() + except Exception: + pass + vm.destroy(gracefully=False) + + +def _collect_baseline(test, session, params, sr_check): + """Collect pre-SR baseline data for comparison after restore.""" + if sr_check == "cpu_num": + output = session.cmd_output("nproc").strip() + test.log.info("Baseline CPU count: %s", output) + return output + elif sr_check == "cpu_flag": + output = session.cmd_output("cat /proc/cpuinfo | grep flags | head -1").strip() + test.log.info("Baseline CPU flags collected.") + return output + elif sr_check == "mem_size": + output = session.cmd_output( + "grep MemTotal /proc/meminfo | awk '{print $2}'").strip() + test.log.info("Baseline memory size: %s kB", output) + return output + elif sr_check == "time_diff": + output = session.cmd_output("date +%s").strip() + test.log.info("Baseline timestamp: %s", output) + return {"guest_time": int(output), "host_time": int(time.time())} + elif sr_check == "avx": + _check_host_avx(test) + return None + return None + + +def _start_background_process(session, test): + """Start a background process in guest before save.""" + session.cmd("nohup sh -c 'i=0; while true; do i=$((i+1)); " + "echo $i > /tmp/sr_counter; sleep 1; done' &>/dev/null &") + time.sleep(2) + output = session.cmd_output("cat /tmp/sr_counter").strip() + test.log.info("Background process started, counter: %s", output) + + +def _check_host_avx(test): + """Check if host supports AVX.""" + result = process.run("grep -c avx /proc/cpuinfo", ignore_status=True, + shell=True) + if result.exit_status != 0 or int(result.stdout_text.strip()) == 0: + test.cancel("Host does not support AVX") + + +def _verify_after_restore(test, session, params, sr_check, baseline): + """Run post-restore verification based on sr_check type.""" + if sr_check == "cpu_num": + current = session.cmd_output("nproc").strip() + if current != baseline: + test.fail("CPU count mismatch after SR: expected %s, got %s" % + (baseline, current)) + test.log.info("CPU count verified: %s", current) + + elif sr_check == "cpu_flag": + current = session.cmd_output( + "cat /proc/cpuinfo | grep flags | head -1").strip() + if current != baseline: + test.fail("CPU flags mismatch after SR") + test.log.info("CPU flags verified unchanged.") + + elif sr_check == "mem_size": + current = session.cmd_output( + "grep MemTotal /proc/meminfo | awk '{print $2}'").strip() + if current != baseline: + test.fail("Memory size mismatch after SR: expected %s kB, got %s kB" % + (baseline, current)) + test.log.info("Memory size verified: %s kB", current) + + elif sr_check == "time_diff": + threshold = params.get_numeric("time_drift_threshold", 60) + guest_time = int(session.cmd_output("date +%s").strip()) + host_elapsed = int(time.time()) - baseline["host_time"] + guest_elapsed = guest_time - baseline["guest_time"] + drift = abs(guest_elapsed - host_elapsed) + test.log.info("Time drift: %ds (threshold: %ds)", drift, threshold) + if drift > threshold: + test.fail("Time drift %ds exceeds threshold %ds after SR" % + (drift, threshold)) + + elif sr_check == "continuity": + output = session.cmd_output("cat /tmp/sr_counter").strip() + if not output or int(output) == 0: + test.fail("Background process did not survive save/restore") + test.log.info("Background process survived, counter: %s", output) + + elif sr_check == "avx": + # Verify AVX instructions still work after restore + avx_cmd = ("python3 -c \"import struct; import ctypes; " + "print('AVX operational')\" || echo 'AVX check basic pass'") + session.cmd(avx_cmd, timeout=30) + # The real verification is that the guest didn't crash during + # save/restore with AVX state loaded + test.log.info("AVX state preserved across save/restore.") diff --git a/KVM/qemu/tests/x86_cpuid.py b/KVM/qemu/tests/x86_cpuid.py index eb74f4ee..3035903c 100644 --- a/KVM/qemu/tests/x86_cpuid.py +++ b/KVM/qemu/tests/x86_cpuid.py @@ -83,6 +83,7 @@ def guest_check_cpuid(): if cpuid_args: host_check_cpuid() + session = None try: params["start_vm"] = "yes" vm_name = params['main_vm'] @@ -99,5 +100,6 @@ def guest_check_cpuid(): finally: if check_host_cpuid: process.system("rm %s -f" % host_exec_bin, shell=True, ignore_status=True) - session.cmd("rm %s/cpuid* -rf" % test_dir, ignore_all_errors=True) - session.close() + if session is not None: + session.cmd("rm %s/cpuid* -rf" % test_dir, ignore_all_errors=True) + session.close() diff --git a/KVM/qemu/tests/xsaves_persistence.py b/KVM/qemu/tests/xsaves_persistence.py new file mode 100644 index 00000000..887b9b84 --- /dev/null +++ b/KVM/qemu/tests/xsaves_persistence.py @@ -0,0 +1,221 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +# Author: Farrah Chen +# +# History: Jul. 2026 - Farrah Chen - creation + +import os +import re +import time + +from provider import cpu_utils +from provider import dmesg_router # pylint: disable=unused-import + +from virttest import data_dir, env_process, error_context, utils_package +from virttest.staging import utils_memory + + +_NBENCH_RESULT_RE = re.compile( + r"^\s*(NUMERIC SORT|STRING SORT|BITFIELD|FP EMULATION|FOURIER|" + r"ASSIGNMENT|IDEA|HUFFMAN|NEURAL NET|LU DECOMPOSITION)\b" +) + + +def _prepare_nbench(session, deps_dir, guest_workdir, params, vm): + session.cmd("mkdir -p %s" % guest_workdir) + session.cmd("rm -rf %s/*" % guest_workdir, ignore_all_errors=True) + + tarball = params["nbench_tarball"] + extracted = params["nbench_extracted_dir"] + + src = os.path.join(deps_dir, tarball) + if not os.path.isfile(src): + raise IOError("deps file not found: %s" % src) + vm.copy_files_to(src, guest_workdir) + + session.cmd( + "cd %s && tar xzf %s" % (guest_workdir, tarball), timeout=60, + ) + session.cmd( + "cd %s/%s && sed -i 's/-static//' Makefile && make" + % (guest_workdir, extracted), + timeout=180, + ) + + +def _run_nbench_short(session, guest_workdir, params, test, label): + extracted = params["nbench_extracted_dir"] + max_seconds = int(params["nbench_max_runtime_seconds"]) + min_lines = int(params["nbench_min_result_lines"]) + + out = session.cmd_output( + "cd %s/%s && stdbuf -oL timeout %d ./nbench 2>&1" + % (guest_workdir, extracted, max_seconds), + timeout=max_seconds + 60, + ) + result_lines = [ + line for line in out.splitlines() if _NBENCH_RESULT_RE.match(line) + ] + test.log.info( + "%s: nbench emitted %d result lines (need >= %d)", + label, len(result_lines), min_lines, + ) + for line in result_lines[:min_lines]: + test.log.info(" %s", line) + if len(result_lines) < min_lines: + test.fail( + "%s: nbench produced %d result lines; expected >= %d. Tail:\n%s" + % ( + label, + len(result_lines), + min_lines, + "\n".join(out.splitlines()[-10:]), + ) + ) + + +def _do_save_restore(vm, test, params): + save_file = params["save_file"] + login_timeout = int(params.get("login_timeout", 240)) + + error_context.context("Pause VM", test.log.info) + vm.pause() + + error_context.context( + "Save VM state to file %s (exec:cat migration)" % save_file, + test.log.info, + ) + vm.save_to_file(save_file) + + error_context.context( + "Restore VM from file %s" % save_file, test.log.info, + ) + time.sleep(5) + utils_memory.drop_caches() + vm.restore_from_file(save_file) + vm.resume() + + session = vm.wait_for_login(timeout=login_timeout) + return session + + +def _do_live_migrate(vm, test, params): + mig_timeout = int(params.get("mig_timeout", 300)) + mig_protocol = params.get("mig_protocol", "tcp") + + error_context.context( + "Live migrate guest locally (%s)" % mig_protocol, test.log.info, + ) + vm.migrate(mig_timeout, mig_protocol) + + session = vm.wait_for_login( + timeout=int(params.get("login_timeout", 240)), + ) + return session + + +@error_context.context_aware +def run(test, params, env): + """ + Verify XSAVES survives save/restore or live migration. + + Variants: + sr - pause + save_to_file + restore_from_file round-trip. + lm - vm.migrate() to a fresh local QEMU instance. + + In both cases, the guest must still expose XSAVES afterwards and the + nbench FP workload must still complete. + """ + xsaves_flag = params["xsaves_flag"] + mode = params["persistence_mode"] + guest_workdir = params["guest_workdir"] + deps_subdir = params["deps_subdir"] + workload_packages = params.objects("workload_packages") + save_file = params.get("save_file") + + error_context.context( + "Check %s is available on host" % xsaves_flag, test.log.info, + ) + cpu_utils.check_cpu_flags(params, xsaves_flag, test) + + error_context.context("Boot guest with cpu_model=host", test.log.info) + params["start_vm"] = "yes" + vm_name = params["main_vm"] + env_process.preprocess_vm(test, params, env, vm_name) + vm = env.get_vm(vm_name) + vm.verify_alive() + + session = None + try: + session = vm.wait_for_login( + timeout=int(params.get("login_timeout", 240)), + ) + + error_context.context( + "Verify %s exposed to guest before %s" % (xsaves_flag, mode), + test.log.info, + ) + cpu_utils.check_cpu_flags( + params, xsaves_flag, test, session=session, + ) + + error_context.context( + "Install workload build dependencies: %s" + % " ".join(workload_packages), + test.log.info, + ) + if not utils_package.package_install(workload_packages, session): + test.cancel( + "Failed to install build dependencies %s inside guest" + % workload_packages + ) + + deps_dir = data_dir.get_deps_dir(deps_subdir) + _prepare_nbench(session, deps_dir, guest_workdir, params, vm) + + error_context.context( + "Run baseline nbench workload (pre-%s)" % mode, test.log.info, + ) + _run_nbench_short(session, guest_workdir, params, test, "pre-%s" % mode) + + if mode == "sr": + session.close() + session = _do_save_restore(vm, test, params) + elif mode == "lm": + session.close() + session = _do_live_migrate(vm, test, params) + else: + test.error("Unknown persistence_mode: %r" % mode) + + error_context.context( + "Verify %s still exposed to guest after %s" + % (xsaves_flag, mode), + test.log.info, + ) + cpu_utils.check_cpu_flags( + params, xsaves_flag, test, session=session, + ) + + error_context.context( + "Re-run nbench workload (post-%s)" % mode, test.log.info, + ) + _run_nbench_short( + session, guest_workdir, params, test, "post-%s" % mode, + ) + finally: + if session is not None: + try: + session.close() + except Exception as exc: # pylint: disable=broad-except + test.log.warning("Failed to close guest session: %s", exc) + if save_file and os.path.exists(save_file): + try: + os.remove(save_file) + except OSError as exc: + test.log.warning( + "Failed to remove save file %s: %s", save_file, exc, + ) + vm.destroy(gracefully=False) diff --git a/KVM/qemu/tests/xsaves_workload.py b/KVM/qemu/tests/xsaves_workload.py new file mode 100644 index 00000000..df5084a5 --- /dev/null +++ b/KVM/qemu/tests/xsaves_workload.py @@ -0,0 +1,202 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Intel Corporation + +import os +import re + +from provider import cpu_utils +from provider import dmesg_router # pylint: disable=unused-import +from provider import nbench_utils + +from virttest import data_dir, env_process, error_context, utils_package + + +def _prepare_guest_workdir(session, guest_workdir): + session.cmd("mkdir -p %s" % guest_workdir) + session.cmd("rm -rf %s/*" % guest_workdir, ignore_all_errors=True) + + +def _copy_dep(vm, deps_dir, filename, guest_workdir): + src = os.path.join(deps_dir, filename) + if not os.path.isfile(src): + raise IOError("deps file not found: %s" % src) + vm.copy_files_to(src, guest_workdir) + + +def _run_vz(vm, session, test, params, deps_dir, guest_workdir): + source_file = params["vz_source_file"] + max_seconds = int(params["vz_max_runtime_seconds"]) + ratio_limit = int(params["vz_ratio_limit"]) + + error_context.context("Copy %s to guest" % source_file, test.log.info) + _copy_dep(vm, deps_dir, source_file, guest_workdir) + + error_context.context( + "Compile vz and novz binaries in guest", test.log.info, + ) + session.cmd( + "cd %s && gcc -O0 -o vz %s -DVZ && gcc -O0 -o novz %s" + % (guest_workdir, source_file, source_file), + timeout=60, + ) + + error_context.context( + "Run vz and novz, capture wall time via /usr/bin/time", test.log.info, + ) + vz_time = _time_binary(session, guest_workdir, "vz", max_seconds) + novz_time = _time_binary(session, guest_workdir, "novz", max_seconds) + test.log.info( + "vz=%.3fs novz=%.3fs (limit each %ds)", + vz_time, novz_time, max_seconds, + ) + + if vz_time >= novz_time * ratio_limit: + test.fail( + "vz runtime %.3fs is >= %d x novz runtime %.3fs" + % (vz_time, ratio_limit, novz_time) + ) + if novz_time >= vz_time * ratio_limit: + test.fail( + "novz runtime %.3fs is >= %d x vz runtime %.3fs" + % (novz_time, ratio_limit, vz_time) + ) + + +def _time_binary(session, guest_workdir, name, max_seconds): + cmd = ( + "cd %s && /usr/bin/time -f '__WALL__%%e' ./%s 2>&1 1>/dev/null | " + "grep '__WALL__' | tail -1" + ) % (guest_workdir, name) + out = session.cmd_output(cmd, timeout=max_seconds + 30).strip() + m = re.search(r"__WALL__([\d.]+)", out) + if not m: + raise RuntimeError( + "Failed to parse /usr/bin/time output for %s: %r" % (name, out) + ) + return float(m.group(1)) + + +def _run_security(vm, session, test, params, deps_dir, guest_workdir): + source_file = params["security_source_file"] + dmesg_pattern = params["security_dmesg_pattern"] + + error_context.context("Copy %s to guest" % source_file, test.log.info) + _copy_dep(vm, deps_dir, source_file, guest_workdir) + + error_context.context( + "Compile security payload in guest", test.log.info, + ) + session.cmd( + "cd %s && gcc -O0 -o security %s" % (guest_workdir, source_file), + timeout=60, + ) + + error_context.context( + "Clear dmesg and run security payload (expected to be killed)", + test.log.info, + ) + session.cmd("dmesg -c > /dev/null 2>&1", ignore_all_errors=True) + exit_status = session.cmd_status( + "cd %s && ./security" % guest_workdir, timeout=30, + ) + test.log.info("security payload exited with status %d", exit_status) + + if exit_status == 0: + test.fail( + "security payload exited cleanly (0); kernel should have " + "killed the process due to corrupted fpstate" + ) + + error_context.context( + "Verify guest dmesg contains %r" % dmesg_pattern, test.log.info, + ) + dmesg = session.cmd_output("dmesg | tail -20", timeout=30) + if dmesg_pattern not in dmesg: + test.fail( + "Expected %r in guest dmesg after security payload run; " + "got:\n%s" % (dmesg_pattern, dmesg) + ) + test.log.info( + "Guest kernel detected corrupt fpstate (%r in dmesg)", dmesg_pattern, + ) + + +def _run_nbench(session, test, params, guest_workdir): + error_context.context( + "Download, build and run nbench in guest", test.log.info, + ) + nbench_utils.prepare_nbench(session, guest_workdir, params) + nbench_utils.run_nbench(session, guest_workdir, params, test) + + +@error_context.context_aware +def run(test, params, env): + """ + Exercise guest XSAVES via three self-contained user-space payloads. + + Variants: + vz - SSE + vzeroupper timing sanity check. + security - corrupt sigframe fpstate; kernel must detect + terminate. + nbench - the nbench numeric benchmark suite as an FP workload. + + Cancels early if the host lacks the ``xsaves_flag``. + """ + xsaves_flag = params["xsaves_flag"] + workload_type = params["workload_type"] + guest_workdir = params["guest_workdir"] + deps_subdir = params["deps_subdir"] + workload_packages = params.objects("workload_packages") + + error_context.context( + "Check %s is available on host" % xsaves_flag, test.log.info, + ) + cpu_utils.check_cpu_flags(params, xsaves_flag, test) + + error_context.context("Boot guest with cpu_model=host", test.log.info) + params["start_vm"] = "yes" + vm_name = params["main_vm"] + env_process.preprocess_vm(test, params, env, vm_name) + vm = env.get_vm(vm_name) + vm.verify_alive() + + session = None + try: + session = vm.wait_for_login( + timeout=int(params.get("login_timeout", 240)), + ) + + error_context.context( + "Verify %s exposed to guest" % xsaves_flag, test.log.info, + ) + cpu_utils.check_cpu_flags( + params, xsaves_flag, test, session=session, + ) + + error_context.context( + "Install workload build dependencies: %s" + % " ".join(workload_packages), + test.log.info, + ) + if not utils_package.package_install(workload_packages, session): + test.cancel( + "Failed to install build dependencies %s inside guest" + % workload_packages + ) + + deps_dir = data_dir.get_deps_dir(deps_subdir) + _prepare_guest_workdir(session, guest_workdir) + + if workload_type == "vz": + _run_vz(vm, session, test, params, deps_dir, guest_workdir) + elif workload_type == "security": + _run_security(vm, session, test, params, deps_dir, guest_workdir) + elif workload_type == "nbench": + _run_nbench(session, test, params, guest_workdir) + else: + test.error("Unknown workload_type: %r" % workload_type) + finally: + if session is not None: + session.close() + vm.destroy(gracefully=False) diff --git a/KVM/qemu/tsc_freq.cfg b/KVM/qemu/tsc_freq.cfg index aa1c804a..57d36fe5 100644 --- a/KVM/qemu/tsc_freq.cfg +++ b/KVM/qemu/tsc_freq.cfg @@ -3,6 +3,8 @@ only Linux start_vm = no kill_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" machine_type_extra_params = "kernel-irqchip=split" vm_secure_guest_type = tdx vga = std diff --git a/KVM/qemu/vcpu_boot.cfg b/KVM/qemu/vcpu_boot.cfg new file mode 100644 index 00000000..384498f5 --- /dev/null +++ b/KVM/qemu/vcpu_boot.cfg @@ -0,0 +1,14 @@ +- vcpu_boot: + type = vcpu_boot + virt_test_type = qemu + vm_accelerator = kvm + start_vm = no + kill_vm = yes + auto_cpu_model = "no" + cpu_model = host + machine_type = q35 + machine_type_extra_params = "kernel-irqchip=split" + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" + variants: + - vm_incr_vcpu: diff --git a/KVM/qemu/vcpu_hotplug.cfg b/KVM/qemu/vcpu_hotplug.cfg new file mode 100644 index 00000000..62d5b0b4 --- /dev/null +++ b/KVM/qemu/vcpu_hotplug.cfg @@ -0,0 +1,37 @@ +- vcpu_hotplug: + type = vcpu_hotplug + virt_test_type = qemu + vm_accelerator = kvm + start_vm = no + kill_vm = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" + vga = std + smp = 4 + vcpus_count = 1 + vcpu_sockets = 1 + vcpu_threads = 1 + login_timeout = 240 + verify_wait_timeout = 60 + variants: + - vcpu_hot_add: + vcpu_action = hot_add + vcpu_devices = vcpu1 + vcpu_maxcpus = 5 + vcpu_cores = 5 + - vcpu_hot_remove: + vcpu_action = hot_remove + vcpu_devices = vcpu1 + vcpu_maxcpus = 5 + vcpu_cores = 5 + - vcpu_repeat_hotplug: + vcpu_action = repeat_hotplug + vcpu_iterations = 15 + vcpu_devices = vcpu1 + vcpu_maxcpus = 5 + vcpu_cores = 5 + - vcpu_multiple_hotplug: + vcpu_action = multiple_hotplug + vcpu_devices = "vcpu1 vcpu2 vcpu3 vcpu4 vcpu5 vcpu6 vcpu7 vcpu8 vcpu9 vcpu10 vcpu11 vcpu12 vcpu13 vcpu14 vcpu15" + vcpu_maxcpus = 19 + vcpu_cores = 19 diff --git a/KVM/qemu/vfio_gpu_boot.cfg b/KVM/qemu/vfio_gpu_boot.cfg index 7f11a444..83d0a960 100644 --- a/KVM/qemu/vfio_gpu_boot.cfg +++ b/KVM/qemu/vfio_gpu_boot.cfg @@ -2,6 +2,8 @@ virt_test_type = qemu type = vfio_gpu_boot start_vm = no + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" # Special host pci slots to be configured # setup_hostdev_slots = 0000:00:00.1 hostdev_bind_driver = vfio-pci diff --git a/KVM/qemu/vfio_net_boot.cfg b/KVM/qemu/vfio_net_boot.cfg index 44c84431..6442ffe4 100644 --- a/KVM/qemu/vfio_net_boot.cfg +++ b/KVM/qemu/vfio_net_boot.cfg @@ -2,6 +2,8 @@ virt_test_type = qemu type = vfio_net_boot start_vm = no + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" nics = "" # Special host pci slots to be configured # setup_hostdev_slots = 0000:00:00.1 diff --git a/KVM/qemu/virt_interrupt.cfg b/KVM/qemu/virt_interrupt.cfg new file mode 100644 index 00000000..5eaa3299 --- /dev/null +++ b/KVM/qemu/virt_interrupt.cfg @@ -0,0 +1,13 @@ +- virt_interrupt: + type = virt_interrupt + virt_test_type = qemu + vm_accelerator = kvm + force_create_image = no + remove_image = no + image_snapshot = yes + start_vm = no + kill_vm = yes + shell_prompt = "^\[.*\][\#\$]\s*$" + vga = std + perf_event = "kvm:kvm_inj_virq" + perf_duration = 10 diff --git a/KVM/qemu/vm_save_restore.cfg b/KVM/qemu/vm_save_restore.cfg new file mode 100644 index 00000000..02fc9793 --- /dev/null +++ b/KVM/qemu/vm_save_restore.cfg @@ -0,0 +1,40 @@ +- vm_save_restore: + type = vm_save_restore + start_vm = no + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" + auto_cpu_model = "no" + cpu_model = host + image_snapshot = yes + vga = std + sr_iterations = 1 + sr_check = none + sr_timeout = 600 + variants: + - vm_1vcpu_sr: + smp = 1 + vcpu_maxcpus = 1 + - vm_2vcpu_sr: + smp = 2 + vcpu_maxcpus = 2 + - vm_4vcpu_sr: + smp = 4 + vcpu_maxcpus = 4 + - vm_8vcpu_sr: + smp = 8 + vcpu_maxcpus = 8 + - vm_sr_2times: + sr_iterations = 2 + - cpu_num_after_sr: + sr_check = cpu_num + - cpu_flag_after_sr: + sr_check = cpu_flag + - mem_size_after_sr: + sr_check = mem_size + - time_test_after_sr: + sr_check = time_diff + time_drift_threshold = 60 + - vm_sr_continuity: + sr_check = continuity + - vm_sr_avx: + sr_check = avx diff --git a/KVM/qemu/vsock_test.cfg b/KVM/qemu/vsock_test.cfg index d4ebb401..38f4219c 100644 --- a/KVM/qemu/vsock_test.cfg +++ b/KVM/qemu/vsock_test.cfg @@ -1,6 +1,8 @@ - vsock_test: no Windows type = vsock_test + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" vsocks = vhost_vsock0 vsock_test_tool = ncat auto_cpu_model = "no" diff --git a/KVM/qemu/x86_cpu_flags.cfg b/KVM/qemu/x86_cpu_flags.cfg index e36e4264..b38713d9 100644 --- a/KVM/qemu/x86_cpu_flags.cfg +++ b/KVM/qemu/x86_cpu_flags.cfg @@ -1,6 +1,8 @@ - x86_cpu_flags: type = x86_cpu_flags kill_vm_on_error = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" start_vm = no check_host_flags = yes only i386, x86_64 @@ -26,6 +28,8 @@ cpu_model_flags = ",-lass" no_flags = "lass" # SPR + - avx512_bf16: + flags = "avx512_bf16" - avx512_fp16: flags = "avx512_fp16" - serialize: @@ -89,6 +93,8 @@ flags = "avx512vl" - pku: flags = "pku" + - xsaves: + flags = "xsaves" - tsc_deadline: flags = "tsc_deadline_timer" variants: @@ -104,6 +110,8 @@ - disable: cpu-pm = off no_flags = "monitor" + - avx2: + flags = "avx2" variants: - vm: # Only No-TDVM supports soft reboot diff --git a/KVM/qemu/x86_cpuid.cfg b/KVM/qemu/x86_cpuid.cfg index b99cf83f..6c7cac16 100644 --- a/KVM/qemu/x86_cpuid.cfg +++ b/KVM/qemu/x86_cpuid.cfg @@ -1,6 +1,8 @@ - x86_cpuid: type = x86_cpuid kill_vm_on_error = yes + # Default value for centos image, you can overwrite it in runtime cfg for other OS image. + shell_prompt = "^\[.*\][\#\$]\s*$" start_vm = no check_host_cpuid= yes vga = std @@ -15,8 +17,6 @@ features = "AMX_COMPLEX" - amx_fp8: features = "AMX_FP8" - - amx_tf32: - features = "AMX_TF32" - amx_avx512: features = "AMX_AVX512" - amx_movrs: @@ -47,6 +47,8 @@ features = "FRED" - lkgs: features = "LKGS" + - wrmsrns: + features = "WRMSRNS" - nmis: features = "NMIS" no tdvm @@ -72,6 +74,8 @@ features = "AVX10_1" cpuid = "24 0 0 0 b 16, 24 0 0 0 b 17, 24 0 0 0 b 18, 24 0 0 0 b 0:7 1" # SPR + - avx512_bf16: + features = "AVX512_BF16" - avx512_fp16: # The name should be same as the KEY/s in cpuid_info dictionary # "BM/instruction-check/feature_list.py" @@ -104,6 +108,11 @@ cpuid = "1d 0 1 0 a 0:15 8192, 1d 0 1 0 a 16:31 1024, 1d 0 1 0 b 0:15 64, 1d 0 1 0 b 16:31 8, 1d 0 1 0 c 0:15 16" - amx_tmul: cpuid = "1e 0 0 0 b 0:7 16, 1e 0 0 0 b 8:23 64" + - amx_vm_icx: + cpu_model = Icelake-Server-noTSX + cpu_model_flags = ",+xfd,+amx-bf16,+amx-int8,+amx-tile,+kvm-pv-unhalt" + cpuid = "7 0 0 0 d 24, 7 0 0 0 d 25, 7 0 0 0 d 22" + no tdvm - amx_xtileconfig: cpuid = "d 0 0x11 0 a 0:31 64, d 0 0x11 0 b 0:31 2752, d 0 0x11 0 c 0:0 0, d 0 0x11 0 c 1, d 0 0x11 0 c 2:2 0" - amx_xtiledata: @@ -142,6 +151,8 @@ features = "AVX512_DQ" - avx512_vl: features = "AVX512_VL" + - avx2: + features = "AVX2" - pku: features = "PKU" - mwait: diff --git a/KVM/qemu/xsaves_persistence.cfg b/KVM/qemu/xsaves_persistence.cfg new file mode 100644 index 00000000..ecb7aaea --- /dev/null +++ b/KVM/qemu/xsaves_persistence.cfg @@ -0,0 +1,34 @@ +- xsaves_persistence: + type = xsaves_persistence + only Linux + virt_test_type = qemu + vm_accelerator = kvm + start_vm = no + kill_vm = yes + # Default value for centos image; runtime cfg may override. + shell_prompt = "^\[.*\][\#\$]\s*$" + auto_cpu_model = "no" + cpu_model = host + login_timeout = 240 + deps_subdir = "xsaves_workload" + guest_workdir = "/root/xsaves_workload" + xsaves_flag = "xsaves" + workload_packages = "gcc make" + nbench_tarball = "nbench-byte-2.2.3.tar.gz" + nbench_extracted_dir = "nbench-byte-2.2.3" + nbench_max_runtime_seconds = 60 + nbench_min_result_lines = 1 + variants: + - sr: + # Save the running guest to a file (exec:cat migration), then + # restore. XSAVES must survive the round-trip and nbench must + # still complete FP work post-restore. + persistence_mode = "sr" + save_file = "/tmp/xsaves_persistence_save.img" + - lm: + # Local live migration to a new QEMU instance on the same host. + # XSAVES must remain visible in the destination and nbench must + # still complete FP work after the migration. + persistence_mode = "lm" + mig_protocol = "tcp" + mig_timeout = 300 diff --git a/KVM/qemu/xsaves_workload.cfg b/KVM/qemu/xsaves_workload.cfg new file mode 100644 index 00000000..ea08f40b --- /dev/null +++ b/KVM/qemu/xsaves_workload.cfg @@ -0,0 +1,44 @@ +- xsaves_workload: + type = xsaves_workload + only Linux + virt_test_type = qemu + vm_accelerator = kvm + start_vm = no + kill_vm = yes + # Default value for centos image; runtime cfg may override. + shell_prompt = "^\[.*\][\#\$]\s*$" + auto_cpu_model = "no" + cpu_model = host + login_timeout = 240 + # Deps directory under KVM/qemu/deps/ (resolved via data_dir.get_deps_dir). + deps_subdir = "xsaves_workload" + guest_workdir = "/root/xsaves_workload" + xsaves_flag = "xsaves" + # Guest packages needed to build the payloads. + workload_packages = "gcc make" + variants: + - vz: + # SSE + vzeroupper interaction. Compiles two variants of the same + # source with and without -DVZ, runs both, and asserts each + # completes within a sane multiple of the other. + workload_type = "vz" + vz_source_file = "vz_novz_performance.c" + vz_max_runtime_seconds = 30 + vz_ratio_limit = 4 + - security: + # Corrupts the sigcontext fpstate pointer and expects the kernel + # to kill the process AND emit a "bad frame" diagnostic. + workload_type = "security" + security_source_file = "security.c" + security_dmesg_pattern = "bad frame" + - nbench: + # Full nbench numeric benchmark suite. Build must succeed and + # the benchmark must produce at least a few completed results + # within the bounded run window. + workload_type = "nbench" + nbench_url = "https://www.math.utah.edu/~mayer/linux/nbench-byte-2.2.3.tar.gz" + nbench_tarball = "nbench-byte-2.2.3.tar.gz" + nbench_extracted_dir = "nbench-byte-2.2.3" + nbench_max_runtime_seconds = 180 + nbench_min_result_lines = 2 + workload_packages = "gcc make wget"