diff --git a/qemu/deps/sev-snp/regular_attestation_workflow.sh b/qemu/deps/sev-snp/regular_attestation_workflow.sh index b79d134f0a..f8149c8355 100644 --- a/qemu/deps/sev-snp/regular_attestation_workflow.sh +++ b/qemu/deps/sev-snp/regular_attestation_workflow.sh @@ -1,5 +1,11 @@ #!/bin/bash -set -e + +check_status() { + if [ $? -ne 0 ]; then + echo "Error: $1" + exit 1 + fi +} # Check for the required CPU model parameter if [[ -z "$1" ]]; then @@ -30,23 +36,22 @@ fetch_retry() { # Verify regular attestation workflow on snp guest snpguest report attestation-report.bin request-data.txt --random +if [[ ! -f attestation-report.bin ]]; then + echo "Error: attestation-report.bin not created." + exit 1 +fi snpguest display report attestation-report.bin - +check_status "Error: Failed display attestation-report." # Fetch cert -set +e -fetch_retry "snpguest fetch ca pem ${cpu_model} ./ -e vcek" -if [[ $? -ne 0 ]]; then - echo "ok" - exit 1 -fi +fetch_retry "snpguest fetch ca -e vcek pem ./ ${cpu_model}" +check_status "Error: Failed to fetch CA certificate." -fetch_retry "snpguest fetch vcek pem ${cpu_model} ./ attestation-report.bin" -if [[ $? -ne 0 ]]; then - exit 1 -fi +fetch_retry "snpguest fetch vcek -p ${cpu_model} pem ./ attestation-report.bin" +check_status "Error: Failed to fetch VCEK certificate." # Verify certs -set -e snpguest verify certs ./ -snpguest verify attestation ./ attestation-report.bin +check_status "Error: Failed to verify certificates." +snpguest verify attestation -p ${cpu_model} ./ attestation-report.bin +check_status "Error: Failed to verify attestation." diff --git a/qemu/deps/sev-snp/snpguest_install.sh b/qemu/deps/sev-snp/snpguest_install.sh new file mode 100644 index 0000000000..eb729bac63 --- /dev/null +++ b/qemu/deps/sev-snp/snpguest_install.sh @@ -0,0 +1,150 @@ +#!/bin/bash + +check_status() { + if [ $? -ne 0 ]; then + echo "Error: $1" + exit 1 + fi +} + +usage() { + echo "Usage: $0 [--repo ] [--branch | --tag ]" + echo " --repo : Git repository URL (default: https://github.com/virtee/snpguest.git)" + echo " --branch : Branch name to checkout (default: main)" + echo " --tag : Tag name to checkout" + echo "Note: --branch and --tag are mutually exclusive." + exit 1 +} + +trap 'echo "Cleaning up..."; rm -rf snpguest; exit 1' ERR + +REPO_URL="https://github.com/virtee/snpguest.git" +BRANCH="main" +TAG="" +while [[ $# -gt 0 ]]; do + case "$1" in + --repo) + REPO_URL="$2" + shift 2 + ;; + --branch) + [ -n "$TAG" ] && { echo "Error: Cannot specify both --branch and --tag"; usage; } + BRANCH="$2" + shift 2 + ;; + --tag) + [ -n "$BRANCH" ] && [ "$BRANCH" != "main" ] && { echo "Error: Cannot specify both --branch and --tag"; usage; } + TAG="$2" + BRANCH="" + shift 2 + ;; + -h|--help) + usage + ;; + *) + echo "Unknown option: $1" + usage + ;; + esac +done + +sudo -n true 2>/dev/null || { echo "Error: sudo privileges required"; exit 1; } + +echo "Validating repository: $REPO_URL..." +git ls-remote "$REPO_URL" >/dev/null 2>&1 +check_status "Invalid or inaccessible repository: $REPO_URL" + +if [ -n "$TAG" ]; then + echo "Validating tag: $TAG..." + git ls-remote --tags "$REPO_URL" | grep -q "refs/tags/$TAG$" + check_status "Tag '$TAG' does not exist in repository" +elif [ -n "$BRANCH" ]; then + echo "Validating branch: $BRANCH..." + git ls-remote --heads "$REPO_URL" | grep -q "refs/heads/$BRANCH$" + check_status "Branch '$BRANCH' does not exist in repository" +fi + +echo "Installing build essentials..." + +if [ -f /etc/os-release ]; then + . /etc/os-release + OS=$ID +else + echo "Cannot detect operating system." + exit 1 +fi + +case "$OS" in + ubuntu|debian) + sudo apt update + check_status "Failed to update package lists" + sudo apt install -y build-essential git curl + check_status "Failed to install build-essential" + ;; + rhel|centos|fedora|rocky|almalinux) + sudo yum groupinstall -y "Development Tools" + check_status "Failed to install Development Tools" + sudo yum install -y curl git + check_status "Failed to install dependencies like curl/git" + ;; + *) + echo "Unsupported operating system: $OS" + exit 1 + ;; +esac + +echo "Prerequisites installed successfully." + +echo "Installing Rust..." +if command -v rustc >/dev/null 2>&1; then + echo "Rust is already installed. Skipping installation." +else + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + check_status "Failed to install Rust" +fi + +if [ -f "$HOME/.cargo/env" ]; then + . "$HOME/.cargo/env" +else + echo "Error: Rust environment file not found" + exit 1 +fi + +rustc --version +check_status "Rust installation verification failed" +echo "Rust installed successfully." + +echo "Cloning snpguest repository from $REPO_URL" +if [ -d snpguest ] +then + echo "Removing previous snpguest directory" + rm -rf snpguest +fi +git clone "$REPO_URL" snpguest +check_status "Failed to clone snpguest repository" +[ -d "snpguest" ] || { echo "Error: snpguest directory not found"; exit 1; } +cd snpguest + +if [ -n "$TAG" ]; then + echo "Checking out tag: $TAG..." + git checkout "tags/$TAG" + check_status "Failed to checkout tag $TAG" +elif [ -n "$BRANCH" ]; then + echo "Checking out branch: $BRANCH..." + git checkout "$BRANCH" + check_status "Failed to checkout branch $BRANCH" +fi + +echo "Building snpguest..." +cargo build --release +check_status "Failed to build snpguest" + +[ -w /usr/local/bin ] || { echo "Error: /usr/local/bin is not writable"; exit 1; } +cp "$PWD/target/release/snpguest" /usr/local/bin +check_status "Failed to copy snpguest to /usr/local/bin" + +snpguest --version +check_status "snpguest installation verification failed" +echo "snpguest installed successfully." + +exit 0 diff --git a/qemu/tests/cfg/snp_attestation.cfg b/qemu/tests/cfg/snp_attestation.cfg new file mode 100644 index 0000000000..dbff2e6cdf --- /dev/null +++ b/qemu/tests/cfg/snp_attestation.cfg @@ -0,0 +1,43 @@ +- snp_attestation: + type = snp_basic_config + only Linux + kill_vm = yes + login_timeout = 240 + start_vm = no + image_snapshot = yes + mem = 8192 + smp = 8 + required_qemu = [9.1.0, ) + vm_secure_guest_type = snp + vm_sev_reduced_phys_bits = 1 + vm_sev_cbitpos = 51 + virtio_dev_disable_legacy = on + vm_mem_backend = memory-backend-memfd + bios_path = /usr/share/edk2/ovmf/OVMF.amdsev.fd + snp_module_path = "/sys/module/kvm_amd/parameters/sev_snp" + module_status = Y y 1 + snp_guest_check = "journalctl|grep -i -w snp" + guest_tool_install = "dnf install -y snpguest" + snpguest_sourcebuild = 1 + attestation_script = regular_attestation_workflow.sh + snpguest_install_script = snpguest_install.sh + guest_dir = /home + guest_cmd = ${guest_dir}/${attestation_script} + host_script = sev-snp/${attestation_script} + snpguest_buildcmd = "${guest_dir}/${snpguest_install_script} --repo https://github.com/virtee/snpguest.git --tag v0.9.1" + snpguest_build_location = sev-snp/${snpguest_install_script} + variants: + - policy_default: + snp_policy = 196608 + vm_secure_guest_object_options = "policy=${snp_policy}" + - policy_debug: + snp_policy = 720896 + vm_secure_guest_object_options = "policy=${snp_policy}" + - policy_singlesocket: + socket_count_cmd = 'lscpu |grep Socket|head -1 | cut -d ":" -f 2 | tr -d " "' + snp_policy = 1245184 + vm_secure_guest_object_options = "policy=${snp_policy}" + - policy_singlesocket_debug: + socket_count_cmd = 'lscpu |grep Socket|head -1 | cut -d ":" -f 2 | tr -d " "' + snp_policy = 1769472 + vm_secure_guest_object_options = "policy=${snp_policy}" diff --git a/qemu/tests/snp_basic_config.py b/qemu/tests/snp_basic_config.py index d9f60920fc..59cd72d741 100644 --- a/qemu/tests/snp_basic_config.py +++ b/qemu/tests/snp_basic_config.py @@ -36,18 +36,36 @@ def run(test, params, env): if int(process.getoutput(socket_count_cmd, shell=True)) != 1: test.cancel("Host cpu has more than 1 socket, skip the case.") - family_id = cpu.get_family() - model_id = cpu.get_model() - dict_cpu = {"251": "milan", "2517": "genoa", "2617": "turin"} - key = str(family_id) + str(model_id) - host_cpu_model = dict_cpu.get(key, "unknown") - + family_id = int(cpu.get_family()) + model_id = int(cpu.get_model()) + dict_cpu = { + "milan": [25, 0, 15], + "genoa": [25, 16, 31], + "bergamo": [25, 160, 175], + "turin": [26, 0, 31], + } + host_cpu_model = None + for platform, values in dict_cpu.items(): + if values[0] == family_id: + if model_id >= values[1] and model_id <= values[2]: + host_cpu_model = platform + if not host_cpu_model: + test.cancel("Unsupported paltform. Requires milan or above.") + test.log.info("Detected platform: %s", host_cpu_model) vm_name = params["main_vm"] vm = env.get_vm(vm_name) vm.create() vm.verify_alive() session = vm.wait_for_login(timeout=timeout) verify_dmesg() + # Check for /dev/sev-guest inside the guest + test.log.info("Checking for SNP attestation support in guest") + rc_code = session.cmd_status("ls /dev/sev-guest") + if rc_code: + test.cancel( + "Error: Unable to open /dev/sev-guest. Guest kernel support for " + "SNP attestation is missing." + ) vm_policy = vm.params.get_numeric("snp_policy") guest_check_cmd = params["snp_guest_check"] sev_guest_info = vm.monitor.query_sev() @@ -67,14 +85,20 @@ def run(test, params, env): host_file = os.path.join(deps_dir, host_script) try: vm.copy_files_to(host_file, guest_dir) - session.cmd_output(params["guest_tool_install"], timeout=240) + if params.get("snpguest_sourcebuild", "0") == "1": + snpguest_build_location = params["snpguest_build_location"] + install_snpguest = os.path.join(deps_dir, snpguest_build_location) + vm.copy_files_to(install_snpguest, guest_dir) + session.cmd_output(params["snpguest_buildcmd"], timeout=360) + else: + session.cmd_output(params["guest_tool_install"], timeout=240) session.cmd_output("chmod 755 %s" % guest_cmd) except Exception as e: test.fail("Guest test preperation fail: %s" % str(e)) guest_cmd = guest_cmd + " " + host_cpu_model s = session.cmd_status(guest_cmd, timeout=360) if s: - test.fail("Guest script error") + test.fail("Guest script error, check the session logs for further details") finally: session.close() vm.destroy()