|
| 1 | +import os |
| 2 | + |
| 3 | +from avocado.utils import cpu |
| 4 | +from virttest import error_context |
| 5 | +from virttest.utils_misc import verify_dmesg |
| 6 | + |
| 7 | + |
| 8 | +@error_context.context_aware |
| 9 | +def run(test, params, env): |
| 10 | + """ |
| 11 | + Qemu CVM (SEV/SEV-ES/SEV-SNP) basic test on AMD Milan and above hosts. |
| 12 | + Steps: |
| 13 | + 1. Verify host SEV/SEV-ES/SEV-SNP support via |
| 14 | + /sys/module/kvm_amd/parameters. |
| 15 | + 2. Check for valid OVMF BIOS path. |
| 16 | + 3. Validate host CPU (Milan, Genoa, Turin). |
| 17 | + 4. Boot a SEV or SEV-ES or SEV-SNP CVM VM. |
| 18 | + 5. Verify CVM enablement in guest via dmesg. |
| 19 | + 6. Check QMP query-sev policy and state. |
| 20 | + :param test: QEMU test object. |
| 21 | + :param params: Dictionary with test parameters: |
| 22 | + - cvm_module_path: Path to SEV* status file |
| 23 | + (e.g., /sys/module/kvm_amd/parameters/sev). |
| 24 | + - module_status: Expected SEV*/SEV-SNP status (e.g., ["1", "Y"]). |
| 25 | + - bios_path: Path to OVMF BIOS for CVM. |
| 26 | + - main_vm: Name of the VM to test. |
| 27 | + - vm_secure_guest_type: Type of CVM ("sev" "seves" or "snp"). |
| 28 | + - vm_sev_policy: Expected SEV/SEV-ES/SNP policy value. |
| 29 | + - cvm_guest_check: Command to verify CVM ("sev" "seves" or "snp") |
| 30 | + enablement in the guest. |
| 31 | + (e.g., 'journalctl|grep -i -w sev-es'). |
| 32 | + - login_timeout: VM login timeout in seconds (default: 240). |
| 33 | + :param env: Dictionary with test environment. |
| 34 | + :raises: test.cancel if host lacks specific cvm capability support, |
| 35 | + BIOS is missing, or CPU is unsupported. |
| 36 | + :raises: test.fail if guest cvm capability verification, |
| 37 | + QMP policy check, or dmesg check fails. |
| 38 | +
|
| 39 | + """ |
| 40 | + error_context.context("Start cvm test", test.log.info) |
| 41 | + timeout = params.get_numeric("login_timeout", 240) |
| 42 | + |
| 43 | + cvm_module_path = params["cvm_module_path"] |
| 44 | + cvm_type = params["vm_secure_guest_type"] |
| 45 | + if os.path.exists(cvm_module_path): |
| 46 | + with open(cvm_module_path) as f: |
| 47 | + output = f.read().strip() |
| 48 | + if output not in params.objects("module_status"): |
| 49 | + test.cancel( |
| 50 | + f"Host support for {cvm_type} capability check failed.") |
| 51 | + else: |
| 52 | + test.cancel(f"Host support for {cvm_type} capability check failed.") |
| 53 | + biospath = params.get("bios_path") |
| 54 | + if not os.path.isfile(biospath): |
| 55 | + test.cancel("bios_path not exist %s." % biospath) |
| 56 | + family_id = int(cpu.get_family()) |
| 57 | + model_id = int(cpu.get_model()) |
| 58 | + supported_cpus = { |
| 59 | + "milan": [25, 0, 15], |
| 60 | + "genoa": [25, 16, 31], |
| 61 | + "bergamo": [25, 160, 175], |
| 62 | + "turin": [26, 0, 31] |
| 63 | + } |
| 64 | + host_platform = None |
| 65 | + for platform, values in supported_cpus.items(): |
| 66 | + if values[0] == family_id: |
| 67 | + if model_id >= values[1] and model_id <= values[2]: |
| 68 | + host_platform = platform |
| 69 | + if not host_platform: |
| 70 | + test.cancel("Unsupported platform. Requires Milan or above.") |
| 71 | + test.log.info("Detected platform: %s", host_platform) |
| 72 | + vm_name = params["main_vm"] |
| 73 | + vm = env.get_vm(vm_name) |
| 74 | + try: |
| 75 | + vm.create() |
| 76 | + vm.verify_alive() |
| 77 | + except Exception as e: |
| 78 | + test.fail("Failed to create VM: %s", str(e)) |
| 79 | + error_context.context("Logging into VM", test.log.info) |
| 80 | + try: |
| 81 | + session = vm.wait_for_login(timeout=timeout) |
| 82 | + except Exception as e: |
| 83 | + test.fail("Failed to login to VM: %s", str(e)) |
| 84 | + # Verify host dmesg for any errors during the guest boot |
| 85 | + verify_dmesg() |
| 86 | + |
| 87 | + cvm_guest_info = vm.monitor.query_sev() |
| 88 | + if not cvm_guest_info: |
| 89 | + test.fail("QMP query-sev returned empty response.") |
| 90 | + test.log.info("QMP cvm info: %s:", cvm_guest_info) |
| 91 | + expected_policy = vm.params.get_numeric("vm_sev_policy") |
| 92 | + if params["vm_secure_guest_type"] == "snp": |
| 93 | + if "snp-policy" not in cvm_guest_info: |
| 94 | + test.fail("QMP snp-policy not found in query-sev response.") |
| 95 | + actual_policy = cvm_guest_info["snp-policy"] |
| 96 | + else: |
| 97 | + if "policy" not in cvm_guest_info: |
| 98 | + test.fail("QMP policy not found in query-sev response.") |
| 99 | + actual_policy = cvm_guest_info["policy"] |
| 100 | + if actual_policy != expected_policy: |
| 101 | + test.fail( |
| 102 | + "QMP cvm policy mismatch: expected %s, " |
| 103 | + "got %s", expected_policy, actual_policy |
| 104 | + ) |
| 105 | + if cvm_guest_info.get("state") != "running": |
| 106 | + test.fail( |
| 107 | + "CVM state is %s, expected 'running'", cvm_guest_info.get('state')) |
| 108 | + error_context.context( |
| 109 | + f"Verifying cvm {cvm_type} capability enablement " |
| 110 | + "in guest", test.log.info |
| 111 | + ) |
| 112 | + guest_check_cmd = params["cvm_guest_check"] |
| 113 | + try: |
| 114 | + return_code, output = session.cmd_status_output( |
| 115 | + guest_check_cmd, timeout=240) |
| 116 | + if return_code != 0: |
| 117 | + test.fail( |
| 118 | + "Guest cvm %s capability check failed with " |
| 119 | + "return code %d: %s", cvm_type, return_code, output |
| 120 | + ) |
| 121 | + test.log.info( |
| 122 | + "Guest cvm %s capability check output: %s", cvm_type, output) |
| 123 | + except Exception as e: |
| 124 | + test.fail("Guest cvm {cvm_type} capability verify fail: %s" % str(e)) |
| 125 | + finally: |
| 126 | + session.close() |
| 127 | + vm.destroy() |
0 commit comments