Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions qemu/tests/cfg/emulated_amd_iommu.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
- emulated_amd_iommu:
type = qemu_pci_passthrough
start_vm = no
only Linux
only x86_64
only HostCpuVendor.amd
pcie_extra_root_port = 0
vt_ulimit_nofile = 65536
usb_devices = ""
auto_cpu_model = "no"
cpu_model = "host"
machine_type = "q35"
machine_type_extra_params = "kernel-irqchip=split"
extra_params = " -global kvm-pit.lost_tick_policy=discard"
vcpu_sockets = 1
vcpu_dies = 1
vcpu_threads = 1
mem = 8G
mode = "x2apic"
variants:
- no_passthrough:
required_qemu = [8.2.90,)
# TODO Add passthrough variant
variants:
- vcpu_512:
smp_fixed = 512
vcpu_maxcpus = 512
vcpu_cores = 512
- vcpu_288:
smp_fixed = 288
vcpu_maxcpus = 288
vcpu_cores = 288
- vcpu_256:
smp_fixed = 256
vcpu_maxcpus = 256
vcpu_cores = 256
- vcpu_254:
smp_fixed = 254
vcpu_maxcpus = 254
vcpu_cores = 254
- vcpu_128:
smp_fixed = 128
vcpu_maxcpus = 128
vcpu_cores = 128
- vcpu_64:
smp_fixed = 64
vcpu_maxcpus = 64
vcpu_cores = 64
variants:
- x2apic_emul_amd_iommu:
cpu_model_flags += ",+x2apic"
extra_params += " -device amd-iommu,intremap=on,xtsup=on"
kvm_probe_module_parameters = "avic=0"
- x2avic_emul_amd_iommu:
cpu_model_flags += ",+x2apic"
extra_params += " -device amd-iommu,intremap=on,xtsup=on"
kvm_probe_module_parameters = "avic=1"
65 changes: 65 additions & 0 deletions qemu/tests/cfg/iommu_accelerated_guest_mode.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
- iommu_accelerated_guest_mode:
type = qemu_pci_passthrough
start_vm = no
only Linux
only x86_64
only HostCpuVendor.amd
pcie_extra_root_port = 0
vt_ulimit_nofile = 65536
usb_devices = ""
auto_cpu_model = "no"
cpu_model = "host"
machine_type = "q35"
machine_type_extra_params = "kernel-irqchip=split"
extra_params = " -global kvm-pit.lost_tick_policy=discard"
vcpu_sockets = 1
vcpu_dies = 1
vcpu_threads = 1
mem = 8G
mode = "x2apic"
kvm_probe_module_parameters = "avic=1"
# Update pci_device with host PCI device/s for passthrough to the guest
# enabling IOMMU functionality validation. eg. pci_device = "0000:01:00.0"
# Note: Without a PCI device input, the guest will still boot, but the
# test won't exercise the host IOMMU.
#pci_device = ""
variants:
- vcpu_512:
smp_fixed = 512
vcpu_maxcpus = 512
vcpu_cores = 512
- vcpu_288:
smp_fixed = 288
vcpu_maxcpus = 288
vcpu_cores = 288
- vcpu_256:
smp_fixed = 256
vcpu_maxcpus = 256
vcpu_cores = 256
- vcpu_254:
smp_fixed = 254
vcpu_maxcpus = 254
vcpu_cores = 254
- vcpu_128:
smp_fixed = 128
vcpu_maxcpus = 128
vcpu_cores = 128
- vcpu_64:
smp_fixed = 64
vcpu_maxcpus = 64
vcpu_cores = 64
variants:
- avic:
mode = "apic"
cpu_model_flags += ",-x2apic"
no vcpu_512, vcpu_288, vcpu_256
- x2avic_ext_apic_id:
cpu_model_flags += ",+x2apic"
cpu_model_flags += ",kvm-msi-ext-dest-id=on"
- x2avic_emul_intel_iommu:
intel_iommu = yes
iommu_eim = on
iommu_intremap = on
guest_iommu_option = pt
iommu_caching_mode = on
cpu_model_flags += ",+x2apic"
69 changes: 69 additions & 0 deletions qemu/tests/cfg/iommu_guest_mode.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
- iommu_guest_mode:
type = qemu_pci_passthrough
start_vm = no
only Linux
only x86_64
only HostCpuVendor.amd
pcie_extra_root_port = 0
vt_ulimit_nofile = 65536
usb_devices = ""
auto_cpu_model = "no"
cpu_model = "host"
machine_type = "q35"
machine_type_extra_params = "kernel-irqchip=split"
extra_params = ""
vcpu_sockets = 1
vcpu_dies = 1
vcpu_threads = 1
mem = 8G
mode = "x2apic"
kvm_probe_module_parameters = "avic=0"
# Update pci_device with host PCI device/s for passthrough to the guest
# enabling IOMMU functionality validation. eg. pci_device = "0000:01:00.0"
# Note: Without a PCI device input, the guest will still boot, but the
# test won't exercise the host IOMMU.
#pci_device = ""
variants:
- vcpu_1024:
smp_fixed = 1024
vcpu_maxcpus = 1024
vcpu_cores = 1024
- vcpu_512:
smp_fixed = 512
vcpu_maxcpus = 512
vcpu_cores = 512
- vcpu_288:
smp_fixed = 288
vcpu_maxcpus = 288
vcpu_cores = 288
- vcpu_256:
smp_fixed = 256
vcpu_maxcpus = 256
vcpu_cores = 256
- vcpu_254:
smp_fixed = 254
vcpu_maxcpus = 254
vcpu_cores = 254
- vcpu_128:
smp_fixed = 128
vcpu_maxcpus = 128
vcpu_cores = 128
- vcpu_64:
smp_fixed = 64
vcpu_maxcpus = 64
vcpu_cores = 64
variants:
- apic:
mode = "apic"
cpu_model_flags += ",-x2apic"
no vcpu_1024, vcpu_512, vcpu_288, vcpu_256
- x2apic_ext_apic_id:
cpu_model_flags += ",+x2apic"
cpu_model_flags += ",kvm-msi-ext-dest-id=on"
- x2apic_emul_intel_iommu:
intel_iommu = yes
iommu_eim = on
iommu_intremap = on
guest_iommu_option = pt
iommu_caching_mode = on
cpu_model_flags += ",+x2apic"
19 changes: 19 additions & 0 deletions qemu/tests/cfg/vfio_pci_passthrough.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
- vfio_pci_passthrough:
type = qemu_pci_passthrough
start_vm = no
only Linux
only x86_64
only HostCpuVendor.amd
pcie_extra_root_port = 0
vt_ulimit_nofile = 65536
usb_devices = ""
auto_cpu_model = "no"
cpu_model = "host"
machine_type = "q35"
extra_params = ""
mem = 8G
# Update pci_device with host PCI device/s for passthrough to the guest
# enabling IOMMU functionality validation. eg. pci_device = "0000:01:00.0"
# Note: Without a PCI device input, the guest will still boot, but the
# test won't exercise the host IOMMU.
#pci_device = ""
174 changes: 174 additions & 0 deletions qemu/tests/qemu_pci_passthrough.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
"""
Guest boot sanity test with passthrough device in different mode
- Apic, X2apic, Avic, X2avic
"""

from avocado.utils import dmesg, linux_modules, pci, process
from virttest import env_process


def run(test, params, env): # pylint: disable=R0915
"""
Guest boot sanity test with passthrough device
Steps:
1. Launch a guest with a vfio pci passthrough device
2. Verify if vfio pci passthrough of device to guest is successful
and guest boots in expected mode - apic, avic, x2apic and x2avic.

:param test: QEMU test object.
:param params: Dictionary with test parameters:
- pci_device: Pci device to passthrough. Default: ""
- mode: Defines guest modes - [apic, x2apic]. Default: x2apic.
- kvm_probe_module_parameters: To enable/disable avic on host.
Possible values ["avic=1", "avic=0"]
- login_timeout: VM login timeout in seconds. Default: 240).
:param env: Dictionary with test environment.
:raises: cancel if
1. At start of test, KVM module is not loaded.
2. KVM, msr, vfio_pci is not built or loadable.
3. pci_device != ""
a. vfio_pci is not built or loadable.
b. Interrupt remapping is not enabled on host. Required for
passthrough.
c. Pci device/s not found on host system.
4. kvm_probe_module_parameters = "avic=1"
a. Host doesnot support expected mode - avic or x2avic.
b. Host doesnot have expected mode enabled for guest - avic
or x2avic.
fails if
1. Pci device cannot be bind to vfio_pci module for passthrough.
(pci_device != "")
2. Unable to login to guest within login timeout.
"""
kvm_probe_module_parameters = params.get("kvm_probe_module_parameters", "")
login_timeout = int(params.get("login_timeout", 240))
pci_device = params.get("pci_device", "")
mode = params.get("mode", "x2apic")
driver_list = []
session = None
vm = None

try:

def check_avic_support():
"""
Check if system supports avic.
"""
cmd = "rdmsr -p 0 0xc00110dd --bitfield 13:13"
out = process.run(cmd, sudo=True, shell=True).stdout_text.strip()
if out == "0":
test.cancel("System doesnot support avic")

def check_x2avic_support():
"""
check if system supports x2avic.
"""
cmd = "rdmsr -p 0 0xc00110dd --bitfield 18:18"
out = process.run(cmd, sudo=True, shell=True).stdout_text.strip()
if out == "0":
test.cancel("System doesnot support x2avic")

def verify_avic_enablement(mode):
"""
Check AVIC and x2AVIC status in dmesg logs diff.

:param mode: Whether apic or x2apic.
"""

# Check for the "avic enabled" in dmesg
if not dmesg.check_kernel_logs("AVIC enabled"):
test.cancel("AVIC not enabled after loading kvm_amd with avic=1")

# Check for the "x2avic enabled" only if the test is "x2apic"
if (not dmesg.check_kernel_logs("x2AVIC enabled")) and (mode == "x2apic"):
test.cancel("x2AVIC not enabled after loading kvm_amd with avic=1.")

def prepare_pci_passthrough():
"""
Validate IOMMU, Interrupt Remapping, and vfio-pci module availability
"""

# Check if interrupt remapping is enabled on system
if not dmesg.check_kernel_logs("AMD-Vi: Interrupt remapping enabled"):
test.cancel("IOMMU interrupt remaping is not enabled")

# Check and load vfio-pci module
linux_modules.configure_module("vfio-pci", "CONFIG_VFIO_PCI")

def guest_system_details(session):
"""
Collect guest system details

:param session: active guest login session
"""
test.log.debug("Debug: %s", session.cmd_output("cat /etc/os-release"))
test.log.debug("Debug: %s", session.cmd_output("uname -a"))
test.log.debug("Debug: %s", session.cmd_output("ls /boot/"))
test.log.debug("Debug: %s", session.cmd_output("lspci -k"))
test.log.debug("Debug: %s", session.cmd_output("lscpu"))
test.log.debug("Debug: %s", session.cmd_output("lsblk"))
test.log.debug("Debug: %s", session.cmd_output("df -h"))
test.log.debug("Debug: %s", session.cmd_output("dmesg"))

# Check system support for avic or x2avic
if kvm_probe_module_parameters == "avic=1":
linux_modules.configure_module("msr", "CONFIG_X86_MSR")
if mode == "apic":
check_avic_support()
if mode == "x2apic":
check_x2avic_support()
# Validate dmesg for avic and x2avic enablement
verify_avic_enablement(mode)

# Passthrough device/s and validate if passthrough is successful
if pci_device != "":
# Perform pre-checks and prereq enablements before pci passthrough
prepare_pci_passthrough()

# Prepare for pci passthrough
for i in range(len(pci_device.split(" "))):
# Check if device input is valid
if pci_device.split(" ")[i] not in pci.get_pci_addresses():
test.cancel("Please provide valid pci device input.")

driver_list.append(pci.get_driver(pci_device.split(" ")[i]))
pci.attach_driver(pci_device.split(" ")[i], "vfio-pci")
params["extra_params"] += (
f" -device vfio-pci,host={pci_device.split(' ')[i]}"
)

params["start_vm"] = "yes"
vm = env.get_vm(params["main_vm"])
try:
env_process.preprocess_vm(test, params, env, params.get("main_vm"))
vm.verify_alive()
except Exception as e:
test.fail(f"Failed to create VM: {str(e)}")
try:
session = vm.wait_for_login(timeout=login_timeout)
except Exception as e:
test.fail(f"Failed to login VM: {str(e)}")
vm.verify_kernel_crash()

# Collect guest system details
guest_system_details(session)
except ValueError as e:
test.fail(f"{e}")
finally:
if session:
session.close()
if vm:
vm.destroy()
try:
if pci_device != "":
for i in range(len(pci_device.split(" "))):
if pci_device.split(" ")[i] not in pci.get_pci_addresses():
break
if driver_list[i] is None:
cur_driver = pci.get_driver(pci_device.split(" ")[i])
if cur_driver is not None:
pci.unbind(cur_driver, pci_device.split(" ")[i])
else:
pci.attach_driver(pci_device.split(" ")[i], driver_list[i])
except ValueError as e:
Comment on lines +163 to +173
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix cleanup: original driver not restored; loop breaks early; may leave devices bound to vfio.

  • Using break skips remaining devices.
  • Restoring logic runs only when driver_list[i] is None, which is inverted, and can call attach_driver(..., None).

Apply this diff to correctly unbind vfio and restore original drivers:

-            if pci_device != "":
-                for i in range(len(pci_device.split(" "))):
-                    if pci_device.split(" ")[i] not in pci.get_pci_addresses():
-                        break
-                    if driver_list[i] is None:
-                        cur_driver = pci.get_driver(pci_device.split(" ")[i])
-                        if cur_driver is not None:
-                            pci.unbind(cur_driver, pci_device.split(" ")[i])
-                        else:
-                            pci.attach_driver(pci_device.split(" ")[i], driver_list[i])
+            if pci_device:
+                devices = [d for d in pci_device.split() if d]
+                for idx, dev in enumerate(devices):
+                    if dev not in pci.get_pci_addresses():
+                        continue
+                    cur_driver = pci.get_driver(dev)
+                    if cur_driver == "vfio-pci":
+                        pci.unbind("vfio-pci", dev)
+                    orig_driver = driver_list[idx] if idx < len(driver_list) else None
+                    if orig_driver:
+                        pci.attach_driver(dev, orig_driver)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if pci_device != "":
for i in range(len(pci_device.split(" "))):
if pci_device.split(" ")[i] not in pci.get_pci_addresses():
break
if driver_list[i] is None:
cur_driver = pci.get_driver(pci_device.split(" ")[i])
if cur_driver is not None:
pci.unbind(cur_driver, pci_device.split(" ")[i])
else:
pci.attach_driver(pci_device.split(" ")[i], driver_list[i])
except ValueError as e:
if pci_device:
devices = [d for d in pci_device.split() if d]
for idx, dev in enumerate(devices):
if dev not in pci.get_pci_addresses():
continue
cur_driver = pci.get_driver(dev)
if cur_driver == "vfio-pci":
pci.unbind("vfio-pci", dev)
orig_driver = driver_list[idx] if idx < len(driver_list) else None
if orig_driver:
pci.attach_driver(dev, orig_driver)
except ValueError as e:
🤖 Prompt for AI Agents
In qemu/tests/qemu_pci_passthrough.py around lines 163-173, the rollback loop
breaks early, repeatedly calls split(" ") and inverts the restore logic (can
call attach_driver with None); fix by precomputing addresses =
pci_device.split(" "), iterate with for i, addr in enumerate(addresses) (no
break — use continue), skip addresses not present via if addr not in
pci.get_pci_addresses(): continue, and on cleanup for each present addr: if
driver_list[i] is not None call pci.attach_driver(addr, driver_list[i]) to
restore the original driver, else obtain cur_driver = pci.get_driver(addr) and
if cur_driver is not None call pci.unbind(cur_driver, addr) to unbind any
temporary driver (this avoids attaching None and ensures all devices are
processed).

test.fail(f"Failed to reset devices after test: Reason {e}")