From 774bc15752175631b052cda3a99157d263f97ee5 Mon Sep 17 00:00:00 2001 From: Srikanth Muppandam Date: Mon, 17 Aug 2026 09:57:53 +0530 Subject: [PATCH 1/3] utils: add shared QTEE PKCS11 helpers Centralize QTEE runtime preparation, TA discovery, result parsing, timeout logging, and service restoration for PKCS11 tests. Add Debian and Ubuntu package mappings so missing QTEE test components can be provisioned before execution. Signed-off-by: Srikanth Muppandam --- Runner/config/pkg_command_map.conf | 18 + Runner/utils/functestlib.sh | 38 ++ Runner/utils/minkipc_pkcs11lib.sh | 562 +++++++++++++++++++++++++++++ 3 files changed, 618 insertions(+) create mode 100755 Runner/utils/minkipc_pkcs11lib.sh diff --git a/Runner/config/pkg_command_map.conf b/Runner/config/pkg_command_map.conf index c7e2d8e3..dc62269b 100755 --- a/Runner/config/pkg_command_map.conf +++ b/Runner/config/pkg_command_map.conf @@ -212,6 +212,24 @@ opkg:Sensors:sns_test=qcom-sensors-api qcom-sensors-core qcom-sensors-registry q debian:package-set:fastrpc=fastrpc-support fastrpc-tests ubuntu:package-set:fastrpc=fastrpc-support fastrpc-tests +# --------------------------------------------------------------------------- +# Qualcomm QTEE PKCS#11 validation packages. +# +# xtest-qtee depends on libckqteec0 and the MinkIPC client libraries. The +# qteesupplicant package is listed explicitly because the PKCS#11 Trusted +# Application needs the running QTEE listener services during validation. +# +# These packages are currently recovered from qli-staging by the PKCS#11 +# runner on Debian and Ubuntu. Yocto, CentOS, and other distributions continue +# to use image-provided components until confirmed package mappings exist. +# --------------------------------------------------------------------------- + +debian:package-set:minkipc-pkcs11=minkipc-qteesupplicant xtest-qtee +ubuntu:package-set:minkipc-pkcs11=minkipc-qteesupplicant xtest-qtee + +debian:xtest_qtee=xtest-qtee +ubuntu:xtest_qtee=xtest-qtee + # --------------------------------------------------------------------------- # Qualcomm userspace-resource-manager package set. # diff --git a/Runner/utils/functestlib.sh b/Runner/utils/functestlib.sh index d113169b..741f1551 100755 --- a/Runner/utils/functestlib.sh +++ b/Runner/utils/functestlib.sh @@ -2484,6 +2484,44 @@ run_with_timeout() { return $status } +# Purpose: Run a command with a timeout and capture stdout and stderr. +# Arguments: +# $1 - Timeout in seconds. +# $2 - Destination log-file path. +# $3... - Command and arguments to execute. +# Side effects: +# Creates or replaces the destination log with command stdout and stderr. +# Returns: +# The command status returned by run_with_timeout(), including a nonzero +# status when the command fails or is terminated after the timeout. +run_with_timeout_log() { + rwtl_timeout="$1" + rwtl_log_file="$2" + shift 2 + + run_with_timeout "$rwtl_timeout" "$@" > "$rwtl_log_file" 2>&1 +} + +# Purpose: Replay every line from a file through the common information logger. +# Arguments: +# $1 - Label prepended to each logged line. +# $2 - Log-file path to replay. +# Output: +# Sends each readable input line through log_info(). +# Returns: +# 0 after replaying the file. An unreadable file is treated as empty. +log_file_with_label() { + lfwl_label="$1" + lfwl_file="$2" + + [ -r "$lfwl_file" ] || return 0 + while IFS= read -r lfwl_line || [ -n "$lfwl_line" ]; do + log_info "[$lfwl_label] $lfwl_line" + done < "$lfwl_file" + + return 0 +} + # Only apply a timeout if TIMEOUT is set; prefer `timeout`; avoid functestlib here runWithTimeoutIfSet() { # Normalize TIMEOUT: treat empty or non-numeric as 0 diff --git a/Runner/utils/minkipc_pkcs11lib.sh b/Runner/utils/minkipc_pkcs11lib.sh new file mode 100755 index 00000000..4477aacc --- /dev/null +++ b/Runner/utils/minkipc_pkcs11lib.sh @@ -0,0 +1,562 @@ +#!/bin/sh +# shellcheck disable=SC2034 + +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause + +# Shared preparation and result helpers for MinkIPC QTEE PKCS#11 tests. +# functestlib.sh and lib_pkg_provider.sh must be sourced before this file. + +MINKIPC_PKCS11_TA_UUID="FD02C9DA-306C-48C7-A49C-BBD827AE86EE" +MINKIPC_PKCS11_STORAGE_TA_UUIDS="B689F2A7-8ADF-477A-9F99-32E90C0AD0A2 731E279E-AAFB-4575-A771-38CAA6F0CCA6" +MINKIPC_PKCS11_EXPECTED_CASES="1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1017 1020 1022 1023 1024" + +MINKIPC_PKCS11_CLIENT_PATH="" +MINKIPC_PKCS11_QTEE_DEVICE="" +MINKIPC_PKCS11_TA_PATH="" +MINKIPC_PKCS11_SFS_UNIT="" +MINKIPC_PKCS11_SUPPLICANT_UNIT="" +MINKIPC_PKCS11_SFS_STARTED_BY_TEST=0 +MINKIPC_PKCS11_SUPPLICANT_STARTED_BY_TEST=0 +MINKIPC_PKCS11_PREP_RESULT="FAIL" +MINKIPC_PKCS11_PREP_MESSAGE="runtime preparation was not completed" + +MINKIPC_PKCS11_CASE_TOTAL="" +MINKIPC_PKCS11_CASE_FAILED="" +MINKIPC_PKCS11_SUBTEST_TOTAL="" +MINKIPC_PKCS11_SUBTEST_FAILED="" +MINKIPC_PKCS11_VALIDATION_MESSAGE="" + +# Purpose: Locate a QTEE TA using the qtee_supplicant autoload search order. +# Arguments: +# $1 - TA UUID without a filename extension. +# Output: +# Prints the first matching .mbn or .b00 path. +# Returns: +# 0 when a TA file is found, otherwise 1. +find_qtee_ta() { + fqta_uuid="$1" + fqta_upper=$(printf '%s\n' "$fqta_uuid" | tr '[:lower:]' '[:upper:]') + fqta_lower=$(printf '%s\n' "$fqta_uuid" | tr '[:upper:]' '[:lower:]') + + for fqta_name in "$fqta_uuid" "$fqta_upper" "$fqta_lower"; do + for fqta_extension in mbn b00; do + fqta_candidate="/data/$fqta_name.$fqta_extension" + if [ -s "$fqta_candidate" ]; then + printf '%s\n' "$fqta_candidate" + return 0 + fi + done + done + + fqta_compatible_file="" + for fqta_path in \ + /sys/firmware/devicetree/base/compatible \ + /proc/device-tree/compatible; do + if [ -r "$fqta_path" ]; then + fqta_compatible_file="$fqta_path" + break + fi + done + + [ -n "$fqta_compatible_file" ] || return 1 + + fqta_compatibles=$(tr '\000' '\n' < "$fqta_compatible_file" 2>/dev/null) + for fqta_compatible in $fqta_compatibles; do + fqta_platform=${fqta_compatible#*,} + [ -n "$fqta_platform" ] || continue + + for fqta_name in "$fqta_uuid" "$fqta_upper" "$fqta_lower"; do + for fqta_extension in mbn b00; do + fqta_candidate="/lib/qtee-tas/$fqta_platform/$fqta_name.$fqta_extension" + if [ -s "$fqta_candidate" ]; then + printf '%s\n' "$fqta_candidate" + return 0 + fi + done + done + done + + return 1 +} + +# Purpose: Prepare packages required by the QTEE PKCS#11 tests. +# Arguments: +# $1 - Normalized operating-system identifier. +# Side effects: +# Debian and Ubuntu may install the minkipc-pkcs11 package set. +# Returns: +# 0 when packages are ready or image-provided components are expected. +# 1 on package preparation failure and sets MINKIPC_PKCS11_PREP_MESSAGE. +minkipc_pkcs11_prepare_packages() { + mpp_os_id="$1" + + case "$mpp_os_id" in + debian|ubuntu) + for mpp_helper in \ + pkg_provider_init \ + pkg_lookup_package_set \ + pkg_ensure_optional_package_set_present; do + if ! command -v "$mpp_helper" >/dev/null 2>&1; then + MINKIPC_PKCS11_PREP_MESSAGE="required package helper is unavailable: $mpp_helper" + return 1 + fi + done + + if ! pkg_lookup_package_set minkipc-pkcs11 >/dev/null 2>&1; then + MINKIPC_PKCS11_PREP_MESSAGE="minkipc-pkcs11 package mapping is unavailable for os=$mpp_os_id" + return 1 + fi + + if ! pkg_provider_init; then + MINKIPC_PKCS11_PREP_MESSAGE="package provider initialization failed" + return 1 + fi + + mpp_old_upgrade="${PKG_PACKAGE_SET_UPGRADE-__unset__}" + PKG_PACKAGE_SET_UPGRADE=0 + export PKG_PACKAGE_SET_UPGRADE + + if ! pkg_ensure_optional_package_set_present \ + minkipc-pkcs11 \ + qli-staging \ + trixie \ + --overlay; then + case "$mpp_old_upgrade" in + __unset__) + unset PKG_PACKAGE_SET_UPGRADE + ;; + *) + PKG_PACKAGE_SET_UPGRADE="$mpp_old_upgrade" + export PKG_PACKAGE_SET_UPGRADE + ;; + esac + MINKIPC_PKCS11_PREP_MESSAGE="failed to prepare the QTEE PKCS#11 package set" + return 1 + fi + + case "$mpp_old_upgrade" in + __unset__) + unset PKG_PACKAGE_SET_UPGRADE + ;; + *) + PKG_PACKAGE_SET_UPGRADE="$mpp_old_upgrade" + export PKG_PACKAGE_SET_UPGRADE + ;; + esac + log_pass "QTEE PKCS#11 package set is ready" + ;; + *) + log_info "Package installation is not enabled for os=$mpp_os_id, using image-provided QTEE components" + ;; + esac + + return 0 +} + +# Purpose: Validate and prepare the complete runtime shared by PKCS#11 tests. +# Arguments: +# $1 - Requested xtest_qtee path or command name, or an empty string. +# $2 - Normalized operating-system identifier. +# Side effects: +# May install packages and start inactive SFS or qtee_supplicant services. +# Outputs: +# Sets MINKIPC_PKCS11_CLIENT_PATH and related runtime-state variables. +# Returns: +# 0 when the runtime is ready. Returns 1 and sets +# MINKIPC_PKCS11_PREP_RESULT and MINKIPC_PKCS11_PREP_MESSAGE otherwise. +minkipc_pkcs11_prepare_runtime() { + mpr_requested_client="$1" + mpr_os_id="$2" + + MINKIPC_PKCS11_CLIENT_PATH="" + MINKIPC_PKCS11_QTEE_DEVICE="" + MINKIPC_PKCS11_TA_PATH="" + MINKIPC_PKCS11_SFS_UNIT="" + MINKIPC_PKCS11_SUPPLICANT_UNIT="" + MINKIPC_PKCS11_SFS_STARTED_BY_TEST=0 + MINKIPC_PKCS11_SUPPLICANT_STARTED_BY_TEST=0 + MINKIPC_PKCS11_PREP_RESULT="FAIL" + MINKIPC_PKCS11_PREP_MESSAGE="runtime preparation was not completed" + + minkipc_pkcs11_prepare_packages "$mpr_os_id" || return 1 + + if [ -n "$mpr_requested_client" ]; then + case "$mpr_requested_client" in + */*) + MINKIPC_PKCS11_CLIENT_PATH="$mpr_requested_client" + ;; + *) + MINKIPC_PKCS11_CLIENT_PATH=$(command -v "$mpr_requested_client" 2>/dev/null || true) + ;; + esac + else + MINKIPC_PKCS11_CLIENT_PATH=$(command -v xtest_qtee 2>/dev/null || true) + fi + + if [ -z "$MINKIPC_PKCS11_CLIENT_PATH" ] || [ ! -x "$MINKIPC_PKCS11_CLIENT_PATH" ]; then + case "$mpr_os_id" in + debian|ubuntu) + MINKIPC_PKCS11_PREP_RESULT="FAIL" + MINKIPC_PKCS11_PREP_MESSAGE="xtest_qtee is missing after package preparation" + ;; + *) + MINKIPC_PKCS11_PREP_RESULT="SKIP" + MINKIPC_PKCS11_PREP_MESSAGE="xtest_qtee is not provisioned on this image" + ;; + esac + return 1 + fi + log_pass "xtest_qtee binary found: $MINKIPC_PKCS11_CLIENT_PATH" + + for mpr_device in /dev/tee0 /dev/qcomtee /dev/qcomtee0 /dev/smcinvoke; do + if [ -c "$mpr_device" ]; then + MINKIPC_PKCS11_QTEE_DEVICE="$mpr_device" + break + fi + done + + if [ -z "$MINKIPC_PKCS11_QTEE_DEVICE" ]; then + MINKIPC_PKCS11_PREP_RESULT="SKIP" + MINKIPC_PKCS11_PREP_MESSAGE="no QCOMTEE or SMCInvoke character device was found" + return 1 + fi + if [ ! -r "$MINKIPC_PKCS11_QTEE_DEVICE" ] || [ ! -w "$MINKIPC_PKCS11_QTEE_DEVICE" ]; then + MINKIPC_PKCS11_PREP_MESSAGE="current user cannot read and write $MINKIPC_PKCS11_QTEE_DEVICE" + return 1 + fi + log_pass "QTEE device is accessible: $MINKIPC_PKCS11_QTEE_DEVICE" + + MINKIPC_PKCS11_TA_PATH=$(find_qtee_ta "$MINKIPC_PKCS11_TA_UUID" 2>/dev/null || true) + if [ -z "$MINKIPC_PKCS11_TA_PATH" ]; then + MINKIPC_PKCS11_PREP_RESULT="SKIP" + MINKIPC_PKCS11_PREP_MESSAGE="PKCS#11 TA $MINKIPC_PKCS11_TA_UUID is not provisioned under /data or the runtime-compatible /lib/qtee-tas directory" + return 1 + fi + log_pass "PKCS#11 Trusted Application found: $MINKIPC_PKCS11_TA_PATH" + + for mpr_sfs in sfsconfig minkipc-sfsconfig sfs-config; do + if systemd_service_exists "$mpr_sfs"; then + MINKIPC_PKCS11_SFS_UNIT="$mpr_sfs" + break + fi + done + + if [ -n "$MINKIPC_PKCS11_SFS_UNIT" ]; then + if systemd_service_is_active "$MINKIPC_PKCS11_SFS_UNIT"; then + log_pass "Secure-filesystem initialization is active: $MINKIPC_PKCS11_SFS_UNIT" + elif [ "$(id -u)" -eq 0 ] && systemd_service_start_safe "$MINKIPC_PKCS11_SFS_UNIT"; then + MINKIPC_PKCS11_SFS_STARTED_BY_TEST=1 + log_pass "Started secure-filesystem initialization for this test: $MINKIPC_PKCS11_SFS_UNIT" + else + MINKIPC_PKCS11_PREP_MESSAGE="secure-filesystem initialization is not successful: $MINKIPC_PKCS11_SFS_UNIT" + return 1 + fi + else + log_warn "No SFS systemd unit was found, validating secure-storage directories directly" + fi + + for mpr_secure_dir in \ + /var/lib/tee/qtee_supplicant \ + /var/lib/qtee_supplicant/vendor/tzstorage; do + if [ ! -d "$mpr_secure_dir" ]; then + MINKIPC_PKCS11_PREP_MESSAGE="required secure-storage directory is missing: $mpr_secure_dir" + return 1 + fi + log_pass "Secure-storage directory exists: $mpr_secure_dir" + done + + if command -v findmnt >/dev/null 2>&1 && findmnt -n /var/lib/tee >/dev/null 2>&1; then + log_pass "Persistent TEE storage is mounted at /var/lib/tee" + else + log_info "/var/lib/tee is not a separate mount, continuing with the available secure-storage path" + fi + + for mpr_supplicant in qteesupplicant qtee-supplicant minkipc-qteesupplicant; do + if systemd_service_exists "$mpr_supplicant"; then + MINKIPC_PKCS11_SUPPLICANT_UNIT="$mpr_supplicant" + break + fi + done + + mpr_supplicant_running=0 + for mpr_process in qtee_supplicant qteesupplicant; do + if get_pid "$mpr_process" >/dev/null 2>&1; then + mpr_supplicant_running=1 + break + fi + done + + if [ -n "$MINKIPC_PKCS11_SUPPLICANT_UNIT" ]; then + if systemd_service_is_active "$MINKIPC_PKCS11_SUPPLICANT_UNIT" || \ + [ "$mpr_supplicant_running" -eq 1 ]; then + log_pass "qtee_supplicant is active: $MINKIPC_PKCS11_SUPPLICANT_UNIT" + elif [ "$(id -u)" -eq 0 ] && systemd_service_start_safe "$MINKIPC_PKCS11_SUPPLICANT_UNIT"; then + MINKIPC_PKCS11_SUPPLICANT_STARTED_BY_TEST=1 + log_pass "Started qtee_supplicant for this test: $MINKIPC_PKCS11_SUPPLICANT_UNIT" + else + MINKIPC_PKCS11_PREP_MESSAGE="qtee_supplicant could not be started: $MINKIPC_PKCS11_SUPPLICANT_UNIT" + return 1 + fi + elif [ "$mpr_supplicant_running" -eq 1 ]; then + log_pass "qtee_supplicant process is running without a systemd unit" + else + MINKIPC_PKCS11_PREP_MESSAGE="qtee_supplicant is not running and no service unit was found" + return 1 + fi + + MINKIPC_PKCS11_PREP_RESULT="PASS" + MINKIPC_PKCS11_PREP_MESSAGE="runtime preparation completed" + return 0 +} + +# Purpose: Restore services started by minkipc_pkcs11_prepare_runtime(). +# Arguments: +# None. Uses the MINKIPC_PKCS11_*_STARTED_BY_TEST state variables. +# Side effects: +# Stops only services that the current test invocation started. +# Returns: +# 0 after attempting restoration. Restoration failures are logged. +minkipc_pkcs11_restore_runtime() { + if [ "$MINKIPC_PKCS11_SUPPLICANT_STARTED_BY_TEST" -eq 1 ] && \ + [ -n "$MINKIPC_PKCS11_SUPPLICANT_UNIT" ]; then + if systemd_service_stop_safe "$MINKIPC_PKCS11_SUPPLICANT_UNIT"; then + log_info "Restored qtee_supplicant service to its original inactive state" + else + log_warn "Could not restore qtee_supplicant service state: $MINKIPC_PKCS11_SUPPLICANT_UNIT" + fi + MINKIPC_PKCS11_SUPPLICANT_STARTED_BY_TEST=0 + fi + + if [ "$MINKIPC_PKCS11_SFS_STARTED_BY_TEST" -eq 1 ] && \ + [ -n "$MINKIPC_PKCS11_SFS_UNIT" ]; then + if systemd_service_stop_safe "$MINKIPC_PKCS11_SFS_UNIT"; then + log_info "Restored SFS service to its original inactive state" + else + log_warn "Could not restore SFS service state: $MINKIPC_PKCS11_SFS_UNIT" + fi + MINKIPC_PKCS11_SFS_STARTED_BY_TEST=0 + fi + + return 0 +} + +# Purpose: Install signal and exit traps that restore PKCS#11 runtime state. +# Arguments: +# None. +# Side effects: +# Replaces the shell EXIT, HUP, INT, and TERM traps. Callers that need extra +# cleanup may replace only the EXIT trap after calling this function. +# Returns: +# 0 after installing the traps. +minkipc_pkcs11_install_cleanup_traps() { + trap 'minkipc_pkcs11_restore_runtime' EXIT + trap 'exit 129' HUP + trap 'exit 130' INT + trap 'exit 143' TERM + + return 0 +} + +# Purpose: Identify storage-test TAs required by xtest_qtee --clear-storage. +# Arguments: +# None. Uses MINKIPC_PKCS11_STORAGE_TA_UUIDS. +# Output: +# Prints one missing TA UUID per line and prints nothing when all are found. +# Returns: +# 0 after checking every required TA. +minkipc_pkcs11_missing_clear_storage_tas() { + for mmcst_uuid in $MINKIPC_PKCS11_STORAGE_TA_UUIDS; do + if ! find_qtee_ta "$mmcst_uuid" >/dev/null 2>&1; then + printf '%s\n' "$mmcst_uuid" + fi + done + + return 0 +} + +# Purpose: Parse the final case and subtest summaries from an xtest_qtee log. +# Arguments: +# $1 - Readable xtest_qtee output log. +# Outputs: +# Sets MINKIPC_PKCS11_CASE_TOTAL, MINKIPC_PKCS11_CASE_FAILED, +# MINKIPC_PKCS11_SUBTEST_TOTAL, and MINKIPC_PKCS11_SUBTEST_FAILED. +# Returns: +# 0 after parsing. Missing summaries are represented by empty output values. +minkipc_pkcs11_parse_summary() { + mps_log="$1" + + mps_case_values=$(awk ' + /test case(s)? of which [0-9]+ failed/ { + for (i = 1; i <= NF; i++) { + if ($i == "test" && $(i + 1) ~ /^case/) { + total = $(i - 1) + failed = $(i + 4) + } + } + } + END { + if (total != "") + print total, failed + } + ' "$mps_log") + + mps_subtest_values=$(awk ' + /subtest(s)? of which [0-9]+ failed/ { + for (i = 1; i <= NF; i++) { + if ($i ~ /^subtests?$/) { + total = $(i - 1) + failed = $(i + 3) + } + } + } + END { + if (total != "") + print total, failed + } + ' "$mps_log") + + MINKIPC_PKCS11_CASE_TOTAL=$(printf '%s\n' "$mps_case_values" | awk '{print $1}') + MINKIPC_PKCS11_CASE_FAILED=$(printf '%s\n' "$mps_case_values" | awk '{print $2}') + MINKIPC_PKCS11_SUBTEST_TOTAL=$(printf '%s\n' "$mps_subtest_values" | awk '{print $1}') + MINKIPC_PKCS11_SUBTEST_FAILED=$(printf '%s\n' "$mps_subtest_values" | awk '{print $2}') + + return 0 +} + +# Purpose: Report expected top-level PKCS#11 cases that did not report OK. +# Arguments: +# $1 - Readable xtest_qtee output log. +# $2 - Optional space-separated case ID manifest. The QTEE manifest is used +# when this argument is omitted or empty. +# Output: +# Prints one missing case ID per line. +# Returns: +# 0 after checking the complete manifest. +minkipc_pkcs11_missing_expected_cases() { + mmec_log="$1" + mmec_expected="${2:-$MINKIPC_PKCS11_EXPECTED_CASES}" + + # Intentional splitting of the validated space-separated case manifest. + # shellcheck disable=SC2086 + for mmec_case in $mmec_expected; do + if ! grep -Eq "^[[:space:]]*pkcs11_${mmec_case}[[:space:]]+OK[[:space:]]*$" "$mmec_log"; then + printf '%s\n' "$mmec_case" + fi + done + + return 0 +} + +# Purpose: Validate an xtest_qtee PKCS#11 summary and expected case manifest. +# Arguments: +# $1 - Readable xtest_qtee output log. +# $2 - Optional space-separated case manifest. Pass "-" to validate only +# the summary without enforcing specific case IDs. +# Outputs: +# Sets parsed summary variables and MINKIPC_PKCS11_VALIDATION_MESSAGE. +# Returns: +# 0 when the run is valid, otherwise 1 with the failure reason in +# MINKIPC_PKCS11_VALIDATION_MESSAGE. +minkipc_pkcs11_validate_log() { + mvl_log="$1" + mvl_expected="${2:-$MINKIPC_PKCS11_EXPECTED_CASES}" + MINKIPC_PKCS11_VALIDATION_MESSAGE="" + + minkipc_pkcs11_parse_summary "$mvl_log" + + if [ -z "$MINKIPC_PKCS11_CASE_TOTAL" ] || \ + [ -z "$MINKIPC_PKCS11_CASE_FAILED" ] || \ + [ -z "$MINKIPC_PKCS11_SUBTEST_TOTAL" ] || \ + [ -z "$MINKIPC_PKCS11_SUBTEST_FAILED" ]; then + MINKIPC_PKCS11_VALIDATION_MESSAGE="xtest_qtee did not emit a complete test summary" + return 1 + fi + + if [ "$MINKIPC_PKCS11_CASE_FAILED" -ne 0 ] || \ + [ "$MINKIPC_PKCS11_SUBTEST_FAILED" -ne 0 ]; then + MINKIPC_PKCS11_VALIDATION_MESSAGE="summary reports cases=$MINKIPC_PKCS11_CASE_TOTAL failed_cases=$MINKIPC_PKCS11_CASE_FAILED subtests=$MINKIPC_PKCS11_SUBTEST_TOTAL failed_subtests=$MINKIPC_PKCS11_SUBTEST_FAILED" + return 1 + fi + + if [ "$MINKIPC_PKCS11_CASE_TOTAL" -eq 0 ]; then + MINKIPC_PKCS11_VALIDATION_MESSAGE="the supplied filters selected no QTEE PKCS#11 test cases" + return 1 + fi + + if [ "$mvl_expected" != "-" ]; then + mvl_missing=$(minkipc_pkcs11_missing_expected_cases "$mvl_log" "$mvl_expected") + if [ -n "$mvl_missing" ]; then + mvl_missing=$(printf '%s\n' "$mvl_missing" | tr '\n' ' ' | sed 's/[[:space:]]*$//') + MINKIPC_PKCS11_VALIDATION_MESSAGE="expected QTEE PKCS#11 cases did not pass: $mvl_missing" + return 1 + fi + fi + + MINKIPC_PKCS11_VALIDATION_MESSAGE="cases=$MINKIPC_PKCS11_CASE_TOTAL subtests=$MINKIPC_PKCS11_SUBTEST_TOTAL" + return 0 +} + +# Purpose: Scan the kernel log for QCOMTEE, SMCInvoke, and RPMB errors. +# Arguments: +# $1 - Directory where dmesg scan artifacts will be stored. +# Side effects: +# Writes artifacts using the shared scan_dmesg_errors() helper. +# Returns: +# 0 when relevant errors are found, otherwise 1. This matches the existing +# scan_dmesg_errors() return convention. +minkipc_pkcs11_scan_dmesg() { + scan_dmesg_errors \ + "$1" \ + 'qcomtee|qcom_tee|qcom-tee|qtee|smcinvoke|rpmb' \ + 'optional|not supported|deferred probe|B689F2A7-8ADF-477A-9F99-32E90C0AD0A2|731E279E-AAFB-4575-A771-38CAA6F0CCA6' +} + +# Purpose: Record, log, and exit with the final result of a PKCS#11 test. +# Arguments: +# $1 - PASS, SKIP, or FAIL result. +# $2 - Human-readable result message. +# $3 - Process exit status. +# Expected globals: +# TESTNAME - Test name written to logs and the result file. +# RES_FILE - Result-file path. +# Side effects: +# Writes the result file and restores services started by the test. +# Returns: +# Does not return. Exits with the status supplied in $3. +finish_test() { + ft_result="$1" + ft_message="$2" + ft_rc="$3" + + case "$ft_result" in + PASS) + log_pass "$TESTNAME PASS: $ft_message" + ;; + SKIP) + log_skip "$TESTNAME SKIP: $ft_message" + ;; + *) + ft_result="FAIL" + log_fail "$TESTNAME FAIL: $ft_message" + ;; + esac + + echo "$TESTNAME $ft_result" > "$RES_FILE" + minkipc_pkcs11_restore_runtime + exit "$ft_rc" +} + +# Purpose: Record an argument or configuration failure before test execution. +# Arguments: +# $1 - Human-readable failure message. +# Expected globals: +# TESTNAME - Test name written to logs and the result file. +# RES_FILE - Result-file path. +# Side effects: +# Delegates result logging, result-file creation, and exit to finish_test(). +# Returns: +# Does not return. Records FAIL and exits with status 1. +write_early_failure() { + finish_test FAIL "$1" 1 +} From f27dc2043a63ab1ad66d9276fb5f9ba403824ad9 Mon Sep 17 00:00:00 2001 From: Srikanth Muppandam Date: Mon, 17 Aug 2026 09:58:02 +0530 Subject: [PATCH 2/3] kernel: add MinkIPC PKCS11 validation Validate the QTEE PKCS11 TA, runtime services, secure storage, expected case manifest, and relevant kernel-log health. Support filters and bounded repeatability in one suite while avoiding inapplicable clear-storage failures on PKCS11-only images. Signed-off-by: Srikanth Muppandam --- .../MinkIPC_PKCS11_Validation.yaml | 27 ++ .../MinkIPC_PKCS11_Validation/README.md | 104 +++++ .../Baseport/MinkIPC_PKCS11_Validation/run.sh | 405 ++++++++++++++++++ 3 files changed, 536 insertions(+) create mode 100644 Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Validation/MinkIPC_PKCS11_Validation.yaml create mode 100644 Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Validation/README.md create mode 100755 Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Validation/run.sh diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Validation/MinkIPC_PKCS11_Validation.yaml b/Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Validation/MinkIPC_PKCS11_Validation.yaml new file mode 100644 index 00000000..0b756a94 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Validation/MinkIPC_PKCS11_Validation.yaml @@ -0,0 +1,27 @@ +metadata: + name: MinkIPC_PKCS11_Validation + format: "Lava-Test Test Definition 1.0" + description: "Validate the Qualcomm QTEE PKCS#11 Trusted Application through MinkIPC using xtest_qtee" + maintainer: + - "Qualcomm Linux Testkit" + os: + - linux + scope: + - functional + +params: + CLIENT: "" + TEE_ID: "" + LEVEL: "0" + TIMEOUT: "1800" + ITERATIONS: "1" + CLEAR_STORAGE: "1" + TEST_IDS: "" + EXCLUDE_IDS: "" + +run: + steps: + - REPO_PATH=$PWD + - cd Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Validation + - XTEST_QTEE="${CLIENT}" XTEST_TEE_ID="${TEE_ID}" XTEST_LEVEL="${LEVEL}" XTEST_TIMEOUT="${TIMEOUT}" XTEST_ITERATIONS="${ITERATIONS}" XTEST_CLEAR_STORAGE="${CLEAR_STORAGE}" XTEST_INCLUDE="${TEST_IDS}" XTEST_EXCLUDE="${EXCLUDE_IDS}" ./run.sh || true + - $REPO_PATH/Runner/utils/send-to-lava.sh MinkIPC_PKCS11_Validation.res diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Validation/README.md b/Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Validation/README.md new file mode 100644 index 00000000..5079bd72 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Validation/README.md @@ -0,0 +1,104 @@ +# MinkIPC QTEE PKCS#11 Validation + +This suite validates the Qualcomm QTEE PKCS#11 Trusted Application through +MinkIPC with `xtest_qtee`. The suite is pinned to `-t pkcs11`; regression, +benchmark, performance, statistics, and internal applets are outside its scope. + +## Prerequisites + +- A Qualcomm QCOMTEE or SMCInvoke device accessible to the test user. +- A running `qtee_supplicant` with TA autoload, filesystem, and RPMB listeners. +- Successful secure-filesystem initialization and writable persistent storage. +- RPMB provisioned for QTEE secure storage. +- The PKCS#11 test TA: + + `FD02C9DA-306C-48C7-A49C-BBD827AE86EE.mbn` + +The TA autoloader first checks `/data`, then derives +`/lib/qtee-tas//` from every runtime device-tree compatible string +after removing the vendor prefix. The runner follows the same lookup rule and +does not hardcode a machine list. + +Debian and Ubuntu recover `minkipc-qteesupplicant` and `xtest-qtee` from the +Qualcomm `qli-staging` trixie suite. Yocto, CentOS, and other distributions use +their image-provided components. + +## Usage + +Run all QTEE-applicable PKCS#11 cases at the baseline level: + +```sh +./run.sh +``` + +Run selected test IDs or exclude a test: + +```sh +./run.sh 1000 1003 1022 +./run.sh -x 1026 +``` + +Run optional higher-level coverage: + +```sh +./run.sh --level 15 +``` + +Select a TEE and override the timeout: + +```sh +./run.sh --tee 1 --timeout 2400 +``` + +Run repeatability coverage without cleanup between iterations: + +```sh +./run.sh --iterations 2 +``` + +Storage cleanup is evaluated only before the first iteration. Every iteration +must independently pass the complete result and expected-case validation. + +Use `./run.sh --help` for the complete wrapper interface. + +## Storage cleanup + +The runner checks for both upstream storage test TAs before attempting +`xtest_qtee --clear-storage`. Source inspection shows that this applet stops at +the first TA it cannot open and clears only the REE and RPMB object namespaces +owned by those storage test TAs. It does not clear the PKCS#11 TA namespace. + +Qualcomm QTEE images may provision only the PKCS#11 TA and omit the upstream +storage test TAs. In that configuration the runner skips the inapplicable +applet instead of invoking it and reporting an expected open-session error. +PKCS#11 case 1003 reinitializes its test token, and the object tests remove +their own persistent test objects. Use `--no-clear-storage` when the pre-run +applet must be suppressed explicitly even if both storage test TAs are present. + +## Result policy + +The run passes only when: + +- `xtest_qtee` exits with status zero. +- The summary reports zero failed test cases and zero failed subtests. +- At least one case runs for a filtered invocation. +- Every expected QTEE PKCS#11 case reports `OK` in an unfiltered invocation. +- The shared kernel-log scanner finds no relevant QCOMTEE or RPMB errors. + +The Qualcomm build exposes 21 PKCS#11 cases. The meta-qcom QTEE guard omits +upstream cases 1016, 1018, 1019, 1021, 1025, 1026, 1027, and 1028. The guard +starts before the shared RSA-AES support used by 1026 and ends after 1027, so +1026 is also absent from the QTEE binary. + +Newer upstream optee_test revisions add cases 1029 for object checksums and +1030 for AES-GCM. They are not part of the optee_test 4.0.0 source integrated +by meta-qcom and should be evaluated when that dependency is upgraded. + +## Related validation + +- `MinkIPC_PKCS11_Multi_Client_Validation` runs safe discovery and session + cases from multiple clients and validates recovery from QTEE TA contention. + +Persistent-object validation across a `qtee_supplicant` restart remains future +work because it requires a helper that can intentionally leave a uniquely +labelled token object and reopen it in a later process. diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Validation/run.sh b/Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Validation/run.sh new file mode 100755 index 00000000..8640698a --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Validation/run.sh @@ -0,0 +1,405 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause + +# ---------- Repo env + helpers ---------- +SCRIPT_DIR="$( + cd "$(dirname "$0")" || exit 1 + pwd +)" +INIT_ENV="" +SEARCH="$SCRIPT_DIR" + +while [ "$SEARCH" != "/" ]; do + if [ -f "$SEARCH/init_env" ]; then + INIT_ENV="$SEARCH/init_env" + break + fi + SEARCH=$(dirname "$SEARCH") +done + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + exit 1 +fi + +# Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. +if [ -z "${__INIT_ENV_LOADED:-}" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" + __INIT_ENV_LOADED=1 +fi + +# shellcheck disable=SC1090 +. "$INIT_ENV" +# shellcheck disable=SC1091 +. "$TOOLS/functestlib.sh" +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/lib_pkg_provider.sh" +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/minkipc_pkcs11lib.sh" + +TESTNAME="MinkIPC_PKCS11_Validation" +RES_FILE="$SCRIPT_DIR/$TESTNAME.res" + +CLIENT_PATH="${XTEST_QTEE:-}" +TEE_ID="${XTEST_TEE_ID:-}" +TEST_LEVEL="${XTEST_LEVEL:-0}" +TIMEOUT_SECONDS="${XTEST_TIMEOUT:-1800}" +ITERATIONS="${XTEST_ITERATIONS:-1}" +CLEAR_STORAGE="${XTEST_CLEAR_STORAGE:-1}" +INCLUDE_TESTS="${XTEST_INCLUDE:-}" +EXCLUDE_TESTS="${XTEST_EXCLUDE:-}" +FILTERED_RUN=0 + +minkipc_pkcs11_install_cleanup_traps + +# Purpose: Print the command-line interface supported by this validation. +# Arguments: +# None. +# Output: +# Writes usage text to standard output. +# Returns: +# 0 after writing usage text to standard output. +usage() { + cat </dev/null || printf '%s\n' unknown) + +log_info "--------------------------------------------------------------------------" +log_info "Starting $TESTNAME" +log_info "OS=$OS_ID arch=$(uname -m 2>/dev/null || printf '%s\n' unknown) level=$TEST_LEVEL iterations=$ITERATIONS timeout=${TIMEOUT_SECONDS}s" + +if ! minkipc_pkcs11_prepare_runtime "$CLIENT_PATH" "$OS_ID"; then + case "$MINKIPC_PKCS11_PREP_RESULT" in + SKIP) + finish_test SKIP "$MINKIPC_PKCS11_PREP_MESSAGE" 0 + ;; + *) + finish_test FAIL "$MINKIPC_PKCS11_PREP_MESSAGE" 1 + ;; + esac +fi +CLIENT_PATH="$MINKIPC_PKCS11_CLIENT_PATH" + +CLEAR_LOG="$SCRIPT_DIR/xtest_qtee_clear_storage.log" +rm -f "$CLEAR_LOG" "$SCRIPT_DIR/xtest_qtee_pkcs11.log" +STALE_ITERATION=1 +while [ "$STALE_ITERATION" -le 10 ]; do + rm -f "$SCRIPT_DIR/xtest_qtee_pkcs11_iteration_${STALE_ITERATION}.log" + STALE_ITERATION=$((STALE_ITERATION + 1)) +done + +if [ "$CLEAR_STORAGE" -eq 1 ]; then + MISSING_STORAGE_TAS=$(minkipc_pkcs11_missing_clear_storage_tas) + if [ -n "$MISSING_STORAGE_TAS" ]; then + MISSING_STORAGE_TAS=$(printf '%s\n' "$MISSING_STORAGE_TAS" | tr '\n' ' ' | sed 's/[[:space:]]*$//') + log_info "Skipping xtest_qtee --clear-storage because required upstream storage-test TAs are not provisioned: $MISSING_STORAGE_TAS" + log_info "The PKCS#11 suite initializes its test token and removes its own test objects" + else + log_info "Running documented pre-test cleanup: $CLIENT_PATH --clear-storage" + run_with_timeout_log 300 "$CLEAR_LOG" "$CLIENT_PATH" --clear-storage + CLEAR_RC=$? + log_file_with_label clear-storage "$CLEAR_LOG" + + if [ "$CLEAR_RC" -eq 0 ]; then + log_pass "xtest_qtee pre-test storage cleanup completed" + else + log_warn "xtest_qtee --clear-storage returned $CLEAR_RC despite both storage-test TAs being present" + log_info "Continuing because this applet does not clear the PKCS#11 TA namespace" + fi + fi +else + log_info "Pre-test storage cleanup was disabled by request" +fi + +set -- "$CLIENT_PATH" -t pkcs11 -l "$TEST_LEVEL" +if [ -n "$TEE_ID" ]; then + set -- "$@" -d "$TEE_ID" +fi +for excluded_test in $EXCLUDE_TESTS; do + set -- "$@" -x "$excluded_test" +done +for included_test in $INCLUDE_TESTS; do + set -- "$@" "$included_test" +done + +log_info "Running QTEE PKCS#11 validation with level=$TEST_LEVEL" +if [ -n "$TEE_ID" ]; then + log_info "Using TEE identifier: $TEE_ID" +fi +if [ -n "$INCLUDE_TESTS" ]; then + log_info "Included test filters: $INCLUDE_TESTS" +fi +if [ -n "$EXCLUDE_TESTS" ]; then + log_info "Excluded test filters: $EXCLUDE_TESTS" +fi + +EXPECTED_CASES="$MINKIPC_PKCS11_EXPECTED_CASES" +if [ "$FILTERED_RUN" -eq 1 ]; then + EXPECTED_CASES="-" +fi + +TOTAL_CASES=0 +TOTAL_SUBTESTS=0 +ITERATION=1 +while [ "$ITERATION" -le "$ITERATIONS" ]; do + if [ "$ITERATIONS" -eq 1 ]; then + RUN_LOG="$SCRIPT_DIR/xtest_qtee_pkcs11.log" + RUN_LABEL="xtest_qtee" + else + RUN_LOG="$SCRIPT_DIR/xtest_qtee_pkcs11_iteration_${ITERATION}.log" + RUN_LABEL="xtest_qtee-$ITERATION" + fi + + log_info "Running QTEE PKCS#11 validation iteration $ITERATION of $ITERATIONS" + run_with_timeout_log "$TIMEOUT_SECONDS" "$RUN_LOG" "$@" + RUN_RC=$? + log_file_with_label "$RUN_LABEL" "$RUN_LOG" + + if [ "$RUN_RC" -ne 0 ]; then + if grep -qiE 'rpmb|storage|token.*not|TEE_ERROR|TEEC_' "$RUN_LOG"; then + log_info "Failure may indicate missing RPMB provisioning, persistent storage, or PKCS#11 TA access" + fi + finish_test FAIL "iteration $ITERATION returned $RUN_RC, see $RUN_LOG" 1 + fi + + if ! minkipc_pkcs11_validate_log "$RUN_LOG" "$EXPECTED_CASES"; then + finish_test FAIL "iteration $ITERATION $MINKIPC_PKCS11_VALIDATION_MESSAGE, see $RUN_LOG" 1 + fi + + log_pass "PKCS#11 iteration $ITERATION passed with cases=$MINKIPC_PKCS11_CASE_TOTAL subtests=$MINKIPC_PKCS11_SUBTEST_TOTAL" + TOTAL_CASES=$((TOTAL_CASES + MINKIPC_PKCS11_CASE_TOTAL)) + TOTAL_SUBTESTS=$((TOTAL_SUBTESTS + MINKIPC_PKCS11_SUBTEST_TOTAL)) + ITERATION=$((ITERATION + 1)) +done + +if minkipc_pkcs11_scan_dmesg "$SCRIPT_DIR/pkcs11_dmesg"; then + finish_test FAIL "relevant QCOMTEE or RPMB errors were found in the kernel log" 1 +fi + +finish_test PASS "iterations=$ITERATIONS cases=$TOTAL_CASES subtests=$TOTAL_SUBTESTS level=$TEST_LEVEL" 0 From ca5174a8fb018b3640e103637ffba384abf3526a Mon Sep 17 00:00:00 2001 From: Srikanth Muppandam Date: Mon, 17 Aug 2026 09:58:23 +0530 Subject: [PATCH 3/3] kernel: add MinkIPC PKCS11 multi-client validation Exercise non-mutating PKCS11 initialization, discovery, and session cases from concurrent xtest_qtee clients. Recover only from serialized QTEE TEEC_ERROR_BUSY session opens with bounded retries while preserving strict failures and per-attempt logs. Signed-off-by: Srikanth Muppandam --- ...inkIPC_PKCS11_Multi_Client_Validation.yaml | 24 ++ .../README.md | 41 ++ .../run.sh | 375 ++++++++++++++++++ 3 files changed, 440 insertions(+) create mode 100644 Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Multi_Client_Validation/MinkIPC_PKCS11_Multi_Client_Validation.yaml create mode 100644 Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Multi_Client_Validation/README.md create mode 100755 Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Multi_Client_Validation/run.sh diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Multi_Client_Validation/MinkIPC_PKCS11_Multi_Client_Validation.yaml b/Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Multi_Client_Validation/MinkIPC_PKCS11_Multi_Client_Validation.yaml new file mode 100644 index 00000000..6db8286e --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Multi_Client_Validation/MinkIPC_PKCS11_Multi_Client_Validation.yaml @@ -0,0 +1,24 @@ +metadata: + name: MinkIPC_PKCS11_Multi_Client_Validation + format: "Lava-Test Test Definition 1.0" + description: "Validate concurrent MinkIPC QTEE PKCS#11 clients and bounded recovery from serialized TA open contention" + maintainer: + - "Qualcomm Linux Testkit" + os: + - linux + scope: + - functional + +params: + CLIENT: "" + TEE_ID: "" + CLIENTS: "2" + TIMEOUT: "300" + BUSY_RETRIES: "5" + +run: + steps: + - REPO_PATH=$PWD + - cd Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Multi_Client_Validation + - XTEST_QTEE="${CLIENT}" XTEST_TEE_ID="${TEE_ID}" XTEST_CLIENT_COUNT="${CLIENTS}" XTEST_TIMEOUT="${TIMEOUT}" XTEST_BUSY_RETRIES="${BUSY_RETRIES}" ./run.sh || true + - $REPO_PATH/Runner/utils/send-to-lava.sh MinkIPC_PKCS11_Multi_Client_Validation.res diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Multi_Client_Validation/README.md b/Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Multi_Client_Validation/README.md new file mode 100644 index 00000000..62c51b5c --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Multi_Client_Validation/README.md @@ -0,0 +1,41 @@ +# MinkIPC QTEE PKCS#11 Multi-Client Validation + +This suite starts multiple `xtest_qtee` processes concurrently to validate +MinkIPC and QTEE PKCS#11 multi-client handling. Each process runs only the +non-mutating cases: + +- 1000: initialize and close the Cryptoki library. +- 1001: enumerate slots, token information, and mechanisms. +- 1002: open, inspect, and close sessions. + +Token initialization and object-mutating cases are intentionally excluded so +the concurrent clients do not race while changing shared persistent state. + +QTEE serializes part of the TA session-open path. When simultaneous processes +race that path, one process may receive `TEEC_ERROR_BUSY` (`0xffff000d`). The +runner treats only that exact open-session response as transient and retries +the affected client with a short bounded backoff. Any other command or result +failure is reported immediately. Every client must eventually pass all three +cases and leave no relevant QCOMTEE or RPMB kernel errors. + +Run two clients with defaults: + +```sh +./run.sh +``` + +Run four clients against a selected TEE: + +```sh +./run.sh --clients 4 --tee 1 +``` + +Change the number of retries allowed after a QTEE busy response: + +```sh +./run.sh --busy-retries 3 +``` + +The default is five retries after the initial attempt. Set +`--busy-retries 0` to require every concurrent client to pass on its first +attempt. diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Multi_Client_Validation/run.sh b/Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Multi_Client_Validation/run.sh new file mode 100755 index 00000000..bb4a4ef4 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_PKCS11_Multi_Client_Validation/run.sh @@ -0,0 +1,375 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause + +# ---------- Repo env + helpers ---------- +SCRIPT_DIR="$( + cd "$(dirname "$0")" || exit 1 + pwd +)" +INIT_ENV="" +SEARCH="$SCRIPT_DIR" + +while [ "$SEARCH" != "/" ]; do + if [ -f "$SEARCH/init_env" ]; then + INIT_ENV="$SEARCH/init_env" + break + fi + SEARCH=$(dirname "$SEARCH") +done + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + exit 1 +fi + +# Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. +if [ -z "${__INIT_ENV_LOADED:-}" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" + __INIT_ENV_LOADED=1 +fi + +# shellcheck disable=SC1090 +. "$INIT_ENV" +# shellcheck disable=SC1091 +. "$TOOLS/functestlib.sh" +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/lib_pkg_provider.sh" +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/minkipc_pkcs11lib.sh" + +TESTNAME="MinkIPC_PKCS11_Multi_Client_Validation" +RES_FILE="$SCRIPT_DIR/$TESTNAME.res" + +CLIENT_PATH="${XTEST_QTEE:-}" +TEE_ID="${XTEST_TEE_ID:-}" +CLIENT_COUNT="${XTEST_CLIENT_COUNT:-2}" +TIMEOUT_SECONDS="${XTEST_TIMEOUT:-300}" +BUSY_RETRIES="${XTEST_BUSY_RETRIES:-5}" +SAFE_CASES="1000 1001 1002" +CLIENT_PIDS="" + +minkipc_pkcs11_install_cleanup_traps + +# Purpose: Print the command-line interface supported by this validation. +# Arguments: +# None. +# Output: +# Writes usage text to standard output. +# Returns: +# 0 after writing usage text to standard output. +usage() { + cat </dev/null; then + kill "$cmc_pid" 2>/dev/null || true + fi + done + + for cmc_pid in ${CLIENT_PIDS:-}; do + wait "$cmc_pid" 2>/dev/null || true + done + + CLIENT_PIDS="" + minkipc_pkcs11_restore_runtime + return 0 +} + +# Purpose: Identify the retryable QTEE serialization response in client output. +# Arguments: +# $1 - xtest_qtee output log to inspect. +# Output: +# None. +# Returns: +# 0 only when a TEEC open-session operation reports the TEEC_ERROR_BUSY +# result code 0xffff000d. Otherwise 1. +multi_client_log_has_qtee_busy() { + mclhqb_log="$1" + + [ -r "$mclhqb_log" ] || return 1 + grep -qiE \ + 'TEEC[ _-]*open[ _-]*session.*(0x)?ffff000d' \ + "$mclhqb_log" +} + +# Purpose: Run one concurrent PKCS#11 client with bounded QTEE busy recovery. +# Arguments: +# $1 - One-based client index used in log names and retry staggering. +# $2 - Stable final log-file path for this client. +# $3 - File where the number of attempts is recorded. +# $4... - xtest_qtee command and arguments. +# Expected globals: +# SCRIPT_DIR, TIMEOUT_SECONDS, BUSY_RETRIES, and SAFE_CASES. +# Side effects: +# Writes one log per attempt, updates the stable final log, and sleeps for a +# short staggered backoff after a retryable QTEE busy response. +# Returns: +# 0 when an attempt exits successfully and passes manifest validation. +# 1 immediately for a non-busy failure or after busy retries are exhausted. +run_multi_client_worker() { + rmcw_index="$1" + rmcw_final_log="$2" + rmcw_attempt_file="$3" + shift 3 + + rmcw_attempt=1 + rmcw_max_attempts=$((BUSY_RETRIES + 1)) + + while [ "$rmcw_attempt" -le "$rmcw_max_attempts" ]; do + rmcw_attempt_log="$SCRIPT_DIR/xtest_qtee_multiclient_${rmcw_index}_attempt_${rmcw_attempt}.log" + printf '%s\n' "$rmcw_attempt" > "$rmcw_attempt_file" + + run_with_timeout_log "$TIMEOUT_SECONDS" "$rmcw_attempt_log" "$@" + rmcw_rc=$? + cp "$rmcw_attempt_log" "$rmcw_final_log" + + if [ "$rmcw_rc" -eq 0 ] && \ + minkipc_pkcs11_validate_log "$rmcw_attempt_log" "$SAFE_CASES"; then + return 0 + fi + + if ! multi_client_log_has_qtee_busy "$rmcw_attempt_log"; then + return 1 + fi + + if [ "$rmcw_attempt" -ge "$rmcw_max_attempts" ]; then + return 1 + fi + + rmcw_delay=$((1 + ((rmcw_index + rmcw_attempt) % 2))) + sleep "$rmcw_delay" + rmcw_attempt=$((rmcw_attempt + 1)) + done + + return 1 +} + +trap 'cleanup_multi_clients' EXIT + +while [ "$#" -gt 0 ]; do + case "$1" in + --client) + [ "$#" -ge 2 ] || write_early_failure "--client requires a value" + CLIENT_PATH="$2" + shift 2 + ;; + --client=*) + CLIENT_PATH=${1#--client=} + shift + ;; + -d|--tee) + [ "$#" -ge 2 ] || write_early_failure "$1 requires a value" + TEE_ID="$2" + shift 2 + ;; + --tee=*) + TEE_ID=${1#--tee=} + shift + ;; + --clients) + [ "$#" -ge 2 ] || write_early_failure "--clients requires a value" + CLIENT_COUNT="$2" + shift 2 + ;; + --clients=*) + CLIENT_COUNT=${1#--clients=} + shift + ;; + --timeout) + [ "$#" -ge 2 ] || write_early_failure "--timeout requires a value" + TIMEOUT_SECONDS="$2" + shift 2 + ;; + --timeout=*) + TIMEOUT_SECONDS=${1#--timeout=} + shift + ;; + --busy-retries) + [ "$#" -ge 2 ] || write_early_failure "--busy-retries requires a value" + BUSY_RETRIES="$2" + shift 2 + ;; + --busy-retries=*) + BUSY_RETRIES=${1#--busy-retries=} + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + write_early_failure "unknown option: $1" + ;; + esac +done + +case "$CLIENT_COUNT" in + ''|*[!0-9]*) + write_early_failure "invalid client count: $CLIENT_COUNT" + ;; +esac +if [ "$CLIENT_COUNT" -lt 2 ] || [ "$CLIENT_COUNT" -gt 8 ]; then + write_early_failure "client count must be between 2 and 8: $CLIENT_COUNT" +fi + +case "$TIMEOUT_SECONDS" in + ''|*[!0-9]*|0) + write_early_failure "invalid timeout: $TIMEOUT_SECONDS" + ;; +esac + +case "$BUSY_RETRIES" in + ''|*[!0-9]*) + write_early_failure "invalid busy retry count: $BUSY_RETRIES" + ;; +esac +if [ "$BUSY_RETRIES" -gt 10 ]; then + write_early_failure "busy retry count must be between 0 and 10: $BUSY_RETRIES" +fi + +if [ -n "$TEE_ID" ]; then + case "$TEE_ID" in + *[!A-Za-z0-9_.:-]*) + write_early_failure "invalid TEE identifier: $TEE_ID" + ;; + esac +fi + +rm -f "$RES_FILE" + +OS_ID=$(pkg_detect_os_id 2>/dev/null || printf '%s\n' unknown) +log_info "--------------------------------------------------------------------------" +log_info "Starting $TESTNAME" +log_info "OS=$OS_ID arch=$(uname -m 2>/dev/null || printf '%s\n' unknown) clients=$CLIENT_COUNT timeout=${TIMEOUT_SECONDS}s busy_retries=$BUSY_RETRIES" + +if ! minkipc_pkcs11_prepare_runtime "$CLIENT_PATH" "$OS_ID"; then + case "$MINKIPC_PKCS11_PREP_RESULT" in + SKIP) + finish_test SKIP "$MINKIPC_PKCS11_PREP_MESSAGE" 0 + ;; + *) + finish_test FAIL "$MINKIPC_PKCS11_PREP_MESSAGE" 1 + ;; + esac +fi +CLIENT_PATH="$MINKIPC_PKCS11_CLIENT_PATH" + +log_info "Launching $CLIENT_COUNT concurrent clients with non-mutating cases: $SAFE_CASES" +log_info "A client is retried only when QTEE reports TEEC_ERROR_BUSY during session open" + +STALE_INDEX=1 +while [ "$STALE_INDEX" -le 8 ]; do + rm -f "$SCRIPT_DIR/xtest_qtee_multiclient_${STALE_INDEX}.log" + rm -f "$SCRIPT_DIR/xtest_qtee_multiclient_${STALE_INDEX}.attempts" + STALE_ATTEMPT=1 + while [ "$STALE_ATTEMPT" -le 11 ]; do + rm -f "$SCRIPT_DIR/xtest_qtee_multiclient_${STALE_INDEX}_attempt_${STALE_ATTEMPT}.log" + STALE_ATTEMPT=$((STALE_ATTEMPT + 1)) + done + STALE_INDEX=$((STALE_INDEX + 1)) +done + +CLIENT_INDEX=1 +while [ "$CLIENT_INDEX" -le "$CLIENT_COUNT" ]; do + CLIENT_LOG="$SCRIPT_DIR/xtest_qtee_multiclient_${CLIENT_INDEX}.log" + CLIENT_ATTEMPTS="$SCRIPT_DIR/xtest_qtee_multiclient_${CLIENT_INDEX}.attempts" + rm -f "$CLIENT_LOG" + + set -- "$CLIENT_PATH" -t pkcs11 + if [ -n "$TEE_ID" ]; then + set -- "$@" -d "$TEE_ID" + fi + for safe_case in $SAFE_CASES; do + set -- "$@" "$safe_case" + done + + run_multi_client_worker \ + "$CLIENT_INDEX" \ + "$CLIENT_LOG" \ + "$CLIENT_ATTEMPTS" \ + "$@" & + CLIENT_PIDS="$CLIENT_PIDS $!" + CLIENT_INDEX=$((CLIENT_INDEX + 1)) +done + +FAILED_CLIENTS="" +CLIENT_INDEX=1 +for client_pid in $CLIENT_PIDS; do + if wait "$client_pid"; then + CLIENT_RC=0 + else + CLIENT_RC=$? + fi + + CLIENT_LOG="$SCRIPT_DIR/xtest_qtee_multiclient_${CLIENT_INDEX}.log" + CLIENT_ATTEMPTS_FILE="$SCRIPT_DIR/xtest_qtee_multiclient_${CLIENT_INDEX}.attempts" + CLIENT_ATTEMPTS=$(sed -n '1p' "$CLIENT_ATTEMPTS_FILE" 2>/dev/null) + case "$CLIENT_ATTEMPTS" in + ''|*[!0-9]*) + CLIENT_ATTEMPTS=1 + ;; + esac + + CLIENT_ATTEMPT=1 + while [ "$CLIENT_ATTEMPT" -le "$CLIENT_ATTEMPTS" ]; do + CLIENT_ATTEMPT_LOG="$SCRIPT_DIR/xtest_qtee_multiclient_${CLIENT_INDEX}_attempt_${CLIENT_ATTEMPT}.log" + log_file_with_label "client-$CLIENT_INDEX-attempt-$CLIENT_ATTEMPT" "$CLIENT_ATTEMPT_LOG" + CLIENT_ATTEMPT=$((CLIENT_ATTEMPT + 1)) + done + + if [ "$CLIENT_RC" -ne 0 ]; then + if multi_client_log_has_qtee_busy "$CLIENT_LOG"; then + log_fail "Concurrent client $CLIENT_INDEX exhausted QTEE busy recovery after $CLIENT_ATTEMPTS attempts" + elif minkipc_pkcs11_validate_log "$CLIENT_LOG" "$SAFE_CASES"; then + log_fail "Concurrent client $CLIENT_INDEX returned $CLIENT_RC" + else + log_fail "Concurrent client $CLIENT_INDEX $MINKIPC_PKCS11_VALIDATION_MESSAGE" + fi + FAILED_CLIENTS="$FAILED_CLIENTS $CLIENT_INDEX" + elif ! minkipc_pkcs11_validate_log "$CLIENT_LOG" "$SAFE_CASES"; then + log_fail "Concurrent client $CLIENT_INDEX $MINKIPC_PKCS11_VALIDATION_MESSAGE" + FAILED_CLIENTS="$FAILED_CLIENTS $CLIENT_INDEX" + else + log_pass "Concurrent client $CLIENT_INDEX passed after attempts=$CLIENT_ATTEMPTS with cases=$MINKIPC_PKCS11_CASE_TOTAL subtests=$MINKIPC_PKCS11_SUBTEST_TOTAL" + fi + + CLIENT_INDEX=$((CLIENT_INDEX + 1)) +done + +CLIENT_PIDS="" + +if [ -n "$FAILED_CLIENTS" ]; then + FAILED_CLIENTS=$(printf '%s\n' "$FAILED_CLIENTS" | sed 's/^[[:space:]]*//') + finish_test FAIL "concurrent clients failed: $FAILED_CLIENTS" 1 +fi + +if minkipc_pkcs11_scan_dmesg "$SCRIPT_DIR/pkcs11_multiclient_dmesg"; then + finish_test FAIL "relevant QCOMTEE or RPMB errors were found in the kernel log" 1 +fi + +finish_test PASS "clients=$CLIENT_COUNT cases_per_client=3 busy_retries=$BUSY_RETRIES" 0