| ############################################################################## |
| # run_qemu translates the ambiguous exit status in Table1 to that in Table2. |
| # Table3 simply documents the complete status table. |
| # |
| # Table1: Before fixup |
| # -------------------- |
| # 0 - Unexpected exit from QEMU (possible signal), or the unittest did |
| # not use debug-exit |
| # 1 - most likely unittest succeeded, or QEMU failed |
| # |
| # Table2: After fixup |
| # ------------------- |
| # 0 - Everything succeeded |
| # 1 - most likely QEMU failed |
| # |
| # Table3: Complete table |
| # ---------------------- |
| # 0 - SUCCESS |
| # 1 - most likely QEMU failed |
| # 2 - most likely a run script failed |
| # 3 - most likely the unittest failed |
| # 124 - most likely the unittest timed out |
| # 127 - most likely the unittest called abort() |
| # 1..127 - FAILURE (could be QEMU, a run script, or the unittest) |
| # >= 128 - Signal (signum = status - 128) |
| ############################################################################## |
| run_qemu () |
| { |
| local stdout errors ret sig |
| |
| initrd_create || return $? |
| echo -n "$@" |
| [ "$ENVIRON_DEFAULT" = "yes" ] && echo -n " #" |
| echo " $INITRD" |
| |
| # stdout to {stdout}, stderr to $errors and stderr |
| exec {stdout}>&1 |
| errors=$("${@}" $INITRD </dev/null 2> >(tee /dev/stderr) > /dev/fd/$stdout) |
| ret=$? |
| exec {stdout}>&- |
| |
| [ $ret -eq 134 ] && echo "QEMU Aborted" >&2 |
| |
| if [ "$errors" ]; then |
| sig=$(grep 'terminating on signal' <<<"$errors") |
| if [ "$sig" ]; then |
| # This is too complex for ${var/search/replace} |
| # shellcheck disable=SC2001 |
| sig=$(sed 's/.*terminating on signal \([0-9][0-9]*\).*/\1/' <<<"$sig") |
| fi |
| fi |
| |
| if [ $ret -eq 0 ]; then |
| # Some signals result in a zero return status, but the |
| # error log tells the truth. |
| if [ "$sig" ]; then |
| ((ret=sig+128)) |
| else |
| # Exiting with zero (non-debugexit) is an error |
| ret=1 |
| fi |
| elif [ $ret -eq 1 ]; then |
| # Even when ret==1 (unittest success) if we also got stderr |
| # logs, then we assume a QEMU failure. Otherwise we translate |
| # status of 1 to 0 (SUCCESS) |
| if [ "$errors" ]; then |
| if ! grep -qvi warning <<<"$errors" ; then |
| ret=0 |
| fi |
| else |
| ret=0 |
| fi |
| fi |
| |
| return $ret |
| } |
| |
| run_qemu_status () |
| { |
| local stdout ret |
| |
| exec {stdout}>&1 |
| lines=$(run_qemu "$@" > >(tee /dev/fd/$stdout)) |
| ret=$? |
| exec {stdout}>&- |
| |
| if [ $ret -eq 1 ]; then |
| testret=$(grep '^EXIT: ' <<<"$lines" | head -n1 | sed 's/.*STATUS=\([0-9][0-9]*\).*/\1/') |
| if [ "$testret" ]; then |
| if [ $testret -eq 1 ]; then |
| ret=0 |
| else |
| ret=$testret |
| fi |
| fi |
| fi |
| |
| return $ret |
| } |
| |
| timeout_cmd () |
| { |
| local s |
| |
| if [ "$TIMEOUT" ] && [ "$TIMEOUT" != "0" ]; then |
| if [ "$CONFIG_EFI" = 'y' ]; then |
| s=${TIMEOUT: -1} |
| if [ "$s" = 's' ]; then |
| TIMEOUT=${TIMEOUT:0:-1} |
| ((TIMEOUT += 10)) # Add 10 seconds for booting UEFI |
| TIMEOUT="${TIMEOUT}s" |
| fi |
| fi |
| echo "timeout -k 1s --foreground $TIMEOUT" |
| fi |
| } |
| |
| qmp () |
| { |
| echo '{ "execute": "qmp_capabilities" }{ "execute":' "$2" '}' | ncat -U $1 |
| } |
| |
| qmp_events () |
| { |
| while ! test -S "$1"; do sleep 0.1; done |
| echo '{ "execute": "qmp_capabilities" }{ "execute": "cont" }' | |
| ncat --no-shutdown -U $1 | |
| jq -c 'select(has("event"))' |
| } |
| |
| filter_quiet_msgs () |
| { |
| grep -v "Now migrate the VM (quiet)" | |
| grep -v "Begin continuous migration (quiet)" | |
| grep -v "End continuous migration (quiet)" | |
| grep -v "Skipped VM migration (quiet)" |
| } |
| |
| seen_migrate_msg () |
| { |
| if [ $skip_migration -eq 1 ]; then |
| grep -q -e "Now migrate the VM" -e "Begin continuous migration" < $1 |
| else |
| grep -q -e "Now migrate the VM" -e "Begin continuous migration" -e "Skipped VM migration" < $1 |
| fi |
| } |
| |
| run_migration () |
| { |
| if ! command -v ncat >/dev/null 2>&1; then |
| echo "${FUNCNAME[0]} needs ncat (netcat)" >&2 |
| return 77 |
| fi |
| |
| migcmdline=("$@") |
| |
| trap 'trap - TERM ; kill 0 ; exit 2' INT TERM |
| trap 'rm -f ${src_out} ${dst_out} ${src_outfifo} ${dst_outfifo} ${dst_incoming} ${src_qmp} ${dst_qmp} ${src_infifo} ${dst_infifo}' RETURN EXIT |
| |
| dst_incoming=$(mktemp -u -t mig-helper-socket-incoming.XXXXXXXXXX) |
| src_out=$(mktemp -t mig-helper-stdout1.XXXXXXXXXX) |
| src_outfifo=$(mktemp -u -t mig-helper-fifo-stdout1.XXXXXXXXXX) |
| dst_out=$(mktemp -t mig-helper-stdout2.XXXXXXXXXX) |
| dst_outfifo=$(mktemp -u -t mig-helper-fifo-stdout2.XXXXXXXXXX) |
| src_qmp=$(mktemp -u -t mig-helper-qmp1.XXXXXXXXXX) |
| dst_qmp=$(mktemp -u -t mig-helper-qmp2.XXXXXXXXXX) |
| src_infifo=$(mktemp -u -t mig-helper-fifo-stdin1.XXXXXXXXXX) |
| dst_infifo=$(mktemp -u -t mig-helper-fifo-stdin2.XXXXXXXXXX) |
| src_qmpout=/dev/null |
| dst_qmpout=/dev/null |
| skip_migration=0 |
| continuous_migration=0 |
| |
| mkfifo ${src_outfifo} |
| mkfifo ${dst_outfifo} |
| |
| # Holding both ends of the input fifo open prevents opens from |
| # blocking and readers getting EOF when a writer closes it. |
| # These fds appear to be unused to shellcheck so quieten the warning. |
| mkfifo ${src_infifo} |
| mkfifo ${dst_infifo} |
| # shellcheck disable=SC2034 |
| exec {src_infifo_fd}<>${src_infifo} |
| # shellcheck disable=SC2034 |
| exec {dst_infifo_fd}<>${dst_infifo} |
| |
| "${migcmdline[@]}" \ |
| -chardev socket,id=mon,path=${src_qmp},server=on,wait=off \ |
| -mon chardev=mon,mode=control \ |
| < ${src_infifo} > ${src_outfifo} & |
| live_pid=$! |
| # Shellcheck complains about useless cat but it is clearer than a |
| # redirect in this case. |
| # shellcheck disable=SC2002 |
| cat ${src_outfifo} | tee ${src_out} | filter_quiet_msgs & |
| |
| # Start the first destination QEMU machine in advance of the test |
| # reaching the migration point, since we expect at least one migration. |
| # Then destination machines are started after the test outputs |
| # subsequent "Now migrate the VM" messages. |
| do_migration || return $? |
| |
| while ps -p ${live_pid} > /dev/null ; do |
| if [ ${continuous_migration} -eq 1 ] ; then |
| do_migration || return $? |
| elif ! seen_migrate_msg ${src_out} ; then |
| sleep 0.1 |
| elif grep -q "Begin continuous migration" < ${src_out} ; then |
| do_migration || return $? |
| elif grep -q "Now migrate the VM" < ${src_out} ; then |
| do_migration || return $? |
| elif [ $skip_migration -eq 0 ] && grep -q "Skipped VM migration" < ${src_out} ; then |
| echo > ${src_infifo} # Resume src and carry on. |
| break; |
| fi |
| done |
| |
| wait ${live_pid} |
| ret=$? |
| |
| while (( $(jobs -r | wc -l) > 0 )); do |
| sleep 0.1 |
| done |
| |
| return $ret |
| } |
| |
| do_migration () |
| { |
| "${migcmdline[@]}" \ |
| -chardev socket,id=mon,path=${dst_qmp},server=on,wait=off \ |
| -mon chardev=mon,mode=control -incoming unix:${dst_incoming} \ |
| < ${dst_infifo} > ${dst_outfifo} & |
| incoming_pid=$! |
| # Shellcheck complains about useless cat but it is clearer than a |
| # redirect in this case. |
| # shellcheck disable=SC2002 |
| cat ${dst_outfifo} | tee ${dst_out} | filter_quiet_msgs & |
| |
| # The test must prompt the user to migrate, so wait for the |
| # "Now migrate VM" or similar console message. |
| while [ ${continuous_migration} -eq 0 ] && ! seen_migrate_msg ${src_out} ; do |
| if ! ps -p ${live_pid} > /dev/null ; then |
| echo > ${dst_infifo} |
| qmp ${dst_qmp} '"quit"'> ${dst_qmpout} 2>/dev/null |
| echo "ERROR: Test exit before migration point." >&2 |
| qmp ${src_qmp} '"quit"'> ${src_qmpout} 2>/dev/null |
| return 3 |
| fi |
| sleep 0.1 |
| done |
| |
| if grep -q "Begin continuous migration" < ${src_out} ; then |
| if [ ${continuous_migration} -eq 1 ] ; then |
| echo > ${dst_infifo} |
| qmp ${dst_qmp} '"quit"'> ${dst_qmpout} 2>/dev/null |
| echo "ERROR: Continuous migration already begun." >&2 |
| qmp ${src_qmp} '"quit"'> ${src_qmpout} 2>/dev/null |
| return 3 |
| fi |
| continuous_migration=1 |
| echo > ${src_infifo} |
| fi |
| |
| # Wait until the destination has created the incoming and qmp sockets |
| while ! [ -S ${dst_incoming} ] ; do sleep 0.1 ; done |
| while ! [ -S ${dst_qmp} ] ; do sleep 0.1 ; done |
| |
| if [ $skip_migration -eq 0 ] && grep -q "Skipped VM migration" < ${src_out} ; then |
| # May not get any migrations, exit to main loop for now... |
| # No migrations today, shut down dst in an orderly manner... |
| if [ ${continuous_migration} -eq 1 ] ; then |
| echo > ${dst_infifo} |
| qmp ${dst_qmp} '"quit"'> ${dst_qmpout} 2>/dev/null |
| echo "ERROR: Can't skip in continuous migration." >&2 |
| qmp ${src_qmp} '"quit"'> ${src_qmpout} 2>/dev/null |
| return 3 |
| fi |
| echo > ${dst_infifo} |
| qmp ${dst_qmp} '"quit"'> ${dst_qmpout} 2>/dev/null |
| echo > ${src_infifo} # Resume src and carry on. |
| skip_migration=1 |
| return 0 |
| fi |
| |
| qmp ${src_qmp} '"migrate", "arguments": { "uri": "unix:'${dst_incoming}'" }' > ${src_qmpout} |
| |
| # Wait for the migration to complete |
| migstatus=$(qmp ${src_qmp} '"query-migrate"' | grep return) |
| while ! grep -q '"completed"' <<<"$migstatus" ; do |
| sleep 0.1 |
| if ! migstatus=$(qmp ${src_qmp} '"query-migrate"'); then |
| echo "ERROR: Querying migration state failed." >&2 |
| echo > ${dst_infifo} |
| qmp ${dst_qmp} '"quit"'> ${dst_qmpout} 2>/dev/null |
| return 2 |
| fi |
| migstatus=$(grep return <<<"$migstatus") |
| if grep -q '"failed"' <<<"$migstatus"; then |
| echo "ERROR: Migration failed." >&2 |
| echo > ${dst_infifo} |
| qmp ${src_qmp} '"quit"'> ${src_qmpout} 2>/dev/null |
| qmp ${dst_qmp} '"quit"'> ${dst_qmpout} 2>/dev/null |
| return 2 |
| fi |
| done |
| |
| qmp ${src_qmp} '"quit"'> ${src_qmpout} 2>/dev/null |
| |
| # Should we end continuous migration? |
| if grep -q "End continuous migration" < ${src_out} ; then |
| if [ ${continuous_migration} -eq 0 ] ; then |
| echo "ERROR: Can't end continuous migration when not started." >&2 |
| echo > ${dst_infifo} |
| qmp ${dst_qmp} '"quit"'> ${dst_qmpout} 2>/dev/null |
| qmp ${src_qmp} '"quit"'> ${src_qmpout} 2>/dev/null |
| return 3 |
| fi |
| continuous_migration=0 |
| echo > ${src_infifo} |
| fi |
| |
| if [ ${continuous_migration} -eq 0 ]; then |
| # keypress to dst so getchar completes and test continues |
| echo > ${dst_infifo} |
| fi |
| |
| # Wait for the incoming socket being removed, ready for next destination |
| while [ -S ${dst_incoming} ] ; do sleep 0.1 ; done |
| |
| wait ${live_pid} |
| ret=$? |
| |
| # Now flip the variables because destination machine becomes source |
| # for the next migration. |
| live_pid=${incoming_pid} |
| tmp=${src_out} |
| src_out=${dst_out} |
| dst_out=${tmp} |
| tmp=${src_infifo} |
| src_infifo=${dst_infifo} |
| dst_infifo=${tmp} |
| tmp=${src_outfifo} |
| src_outfifo=${dst_outfifo} |
| dst_outfifo=${tmp} |
| tmp=${src_qmp} |
| src_qmp=${dst_qmp} |
| dst_qmp=${tmp} |
| |
| return $ret |
| } |
| |
| run_panic () |
| { |
| if ! command -v ncat >/dev/null 2>&1; then |
| echo "${FUNCNAME[0]} needs ncat (netcat)" >&2 |
| return 77 |
| fi |
| |
| if ! command -v jq >/dev/null 2>&1; then |
| echo "${FUNCNAME[0]} needs jq" >&2 |
| return 77 |
| fi |
| |
| trap 'trap - TERM ; kill 0 ; exit 2' INT TERM |
| trap 'rm -f ${qmp}' RETURN EXIT |
| |
| qmp=$(mktemp -u -t panic-qmp.XXXXXXXXXX) |
| |
| # start VM stopped so we don't miss any events |
| "$@" -chardev socket,id=mon,path=${qmp},server=on,wait=off \ |
| -mon chardev=mon,mode=control -S & |
| |
| panic_event_count=$(qmp_events ${qmp} | jq -c 'select(.event == "GUEST_PANICKED")' | wc -l) |
| if [ "$panic_event_count" -lt 1 ]; then |
| echo "FAIL: guest did not panic" |
| ret=3 |
| else |
| # some QEMU versions report multiple panic events |
| echo "PASS: guest panicked" |
| ret=1 |
| fi |
| |
| return $ret |
| } |
| |
| migration_cmd () |
| { |
| if [ "$MIGRATION" = "yes" ]; then |
| echo "run_migration" |
| fi |
| } |
| |
| panic_cmd () |
| { |
| if [ "$PANIC" = "yes" ]; then |
| echo "run_panic" |
| fi |
| } |
| |
| search_qemu_binary () |
| { |
| local save_path=$PATH |
| local qemucmd qemu |
| |
| : "${QEMU_ARCH:=$ARCH_NAME}" |
| |
| export PATH=$PATH:/usr/libexec |
| for qemucmd in ${QEMU:-qemu-system-$QEMU_ARCH qemu-kvm}; do |
| if $qemucmd --help 2>/dev/null | grep -q 'QEMU'; then |
| qemu="$qemucmd" |
| break |
| fi |
| done |
| |
| if [ -z "$qemu" ]; then |
| echo "A QEMU binary was not found." >&2 |
| echo "You can set a custom location by using the QEMU=<path> environment variable." >&2 |
| return 2 |
| fi |
| command -v $qemu |
| export PATH=$save_path |
| } |
| |
| initrd_cleanup () |
| { |
| rm -f $KVM_UNIT_TESTS_ENV |
| if [ "$KVM_UNIT_TESTS_ENV_OLD" ]; then |
| export KVM_UNIT_TESTS_ENV |
| KVM_UNIT_TESTS_ENV="$KVM_UNIT_TESTS_ENV_OLD" |
| else |
| unset KVM_UNIT_TESTS_ENV |
| fi |
| unset KVM_UNIT_TESTS_ENV_OLD |
| } |
| |
| initrd_create () |
| { |
| if [ "$ENVIRON_DEFAULT" = "yes" ]; then |
| trap_exit_push 'initrd_cleanup' |
| [ -f "$KVM_UNIT_TESTS_ENV" ] && export KVM_UNIT_TESTS_ENV_OLD="$KVM_UNIT_TESTS_ENV" |
| export KVM_UNIT_TESTS_ENV |
| KVM_UNIT_TESTS_ENV=$(mktemp) |
| env_params |
| env_file |
| env_errata || return $? |
| fi |
| |
| unset INITRD |
| [ -f "$KVM_UNIT_TESTS_ENV" ] && INITRD="-initrd $KVM_UNIT_TESTS_ENV" |
| |
| return 0 |
| } |
| |
| env_add_params () |
| { |
| local p |
| |
| for p in "$@"; do |
| if eval test -v $p; then |
| eval export "$p" |
| else |
| eval export "$p=" |
| fi |
| grep "^$p=" <(env) >>$KVM_UNIT_TESTS_ENV |
| done |
| } |
| |
| env_params () |
| { |
| local qemu have_qemu |
| local _ rest |
| |
| qemu=$(search_qemu_binary) && have_qemu=1 |
| |
| if [ "$have_qemu" ]; then |
| if [ -n "$ACCEL" ] || [ -n "$QEMU_ACCEL" ]; then |
| [ -n "$ACCEL" ] && QEMU_ACCEL=$ACCEL |
| fi |
| QEMU_VERSION_STRING="$($qemu -h | head -1)" |
| # Shellcheck does not see QEMU_MAJOR|MINOR|MICRO are used |
| # shellcheck disable=SC2034 |
| IFS='[ .]' read -r _ _ _ QEMU_MAJOR QEMU_MINOR QEMU_MICRO rest <<<"$QEMU_VERSION_STRING" |
| fi |
| env_add_params QEMU_ACCEL QEMU_VERSION_STRING QEMU_MAJOR QEMU_MINOR QEMU_MICRO |
| |
| KERNEL_VERSION_STRING=$(uname -r) |
| IFS=. read -r KERNEL_VERSION KERNEL_PATCHLEVEL rest <<<"$KERNEL_VERSION_STRING" |
| IFS=- read -r KERNEL_SUBLEVEL KERNEL_EXTRAVERSION <<<"$rest" |
| KERNEL_SUBLEVEL=${KERNEL_SUBLEVEL%%[!0-9]*} |
| KERNEL_EXTRAVERSION=${KERNEL_EXTRAVERSION%%[!0-9]*} |
| ! [[ $KERNEL_SUBLEVEL =~ ^[0-9]+$ ]] && unset $KERNEL_SUBLEVEL |
| ! [[ $KERNEL_EXTRAVERSION =~ ^[0-9]+$ ]] && unset $KERNEL_EXTRAVERSION |
| env_add_params KERNEL_VERSION_STRING KERNEL_VERSION KERNEL_PATCHLEVEL KERNEL_SUBLEVEL KERNEL_EXTRAVERSION |
| } |
| |
| env_file () |
| { |
| local line var |
| |
| [ ! -f "$KVM_UNIT_TESTS_ENV_OLD" ] && return |
| |
| grep -E '^[[:blank:]]*[[:alpha:]_][[:alnum:]_]*=' "$KVM_UNIT_TESTS_ENV_OLD" | while IFS= read -r line ; do |
| var=${line%%=*} |
| if ! grep -q "^$var=" $KVM_UNIT_TESTS_ENV; then |
| eval export "$line" |
| grep "^$var=" <(env) >>$KVM_UNIT_TESTS_ENV |
| fi |
| done |
| } |
| |
| env_errata () |
| { |
| local new_env |
| |
| if [ "$ACCEL" = "tcg" ]; then |
| export "ERRATA_FORCE=y" |
| elif [ "$ERRATATXT" ] && [ ! -f "$ERRATATXT" ]; then |
| echo "$ERRATATXT not found. (ERRATATXT=$ERRATATXT)" >&2 |
| return 2 |
| elif [ "$ERRATATXT" ]; then |
| env_generate_errata |
| fi |
| new_env=$(sort <(env | grep '^ERRATA_') <(grep '^ERRATA_' $KVM_UNIT_TESTS_ENV) | uniq -u) |
| echo "$new_env" >>$KVM_UNIT_TESTS_ENV |
| } |
| |
| env_generate_errata () |
| { |
| local line commit minver errata rest v p s x have |
| |
| for line in $(grep -v '^#' "$ERRATATXT" | tr -d '[:blank:]' | cut -d: -f1,2); do |
| commit=${line%:*} |
| minver=${line#*:} |
| |
| test -z "$commit" && continue |
| errata="ERRATA_$commit" |
| [ -n "${!errata}" ] && continue |
| |
| IFS=. read -r v p rest <<<"$minver" |
| IFS=- read -r s x <<<"$rest" |
| s=${s%%[!0-9]*} |
| x=${x%%[!0-9]*} |
| |
| if ! [[ $v =~ ^[0-9]+$ ]] || ! [[ $p =~ ^[0-9]+$ ]]; then |
| echo "Bad minimum kernel version in $ERRATATXT, $minver" |
| return 2 |
| fi |
| ! [[ $s =~ ^[0-9]+$ ]] && unset $s |
| ! [[ $x =~ ^[0-9]+$ ]] && unset $x |
| |
| if (( $KERNEL_VERSION > $v || |
| ($KERNEL_VERSION == $v && $KERNEL_PATCHLEVEL > $p) )); then |
| have=y |
| elif (( $KERNEL_VERSION == $v && $KERNEL_PATCHLEVEL == $p )); then |
| if [ "$KERNEL_SUBLEVEL" ] && [ "$s" ]; then |
| if (( $KERNEL_SUBLEVEL > $s )); then |
| have=y |
| elif (( $KERNEL_SUBLEVEL == $s )); then |
| if [ "$KERNEL_EXTRAVERSION" ] && [ "$x" ]; then |
| if (( $KERNEL_EXTRAVERSION >= $x )); then |
| have=y |
| else |
| have=n |
| fi |
| elif [ "$x" ] && (( $x != 0 )); then |
| have=n |
| else |
| have=y |
| fi |
| else |
| have=n |
| fi |
| elif [ "$s" ] && (( $s != 0 )); then |
| have=n |
| else |
| have=y |
| fi |
| else |
| have=n |
| fi |
| eval export "$errata=$have" |
| done |
| } |
| |
| trap_exit_push () |
| { |
| local old_exit |
| |
| old_exit=$(trap -p EXIT | sed "s/^[^']*'//;s/'[^']*$//") |
| trap -- "$1; $old_exit" EXIT |
| } |
| |
| kvm_available () |
| { |
| [ -c /dev/kvm ] || |
| return 1 |
| |
| [ "$HOST" = "$ARCH_NAME" ] || |
| ( [ "$HOST" = aarch64 ] && [ "$ARCH" = arm ] ) || |
| ( [ "$HOST" = x86_64 ] && [ "$ARCH" = i386 ] ) |
| } |
| |
| hvf_available () |
| { |
| [ "$(sysctl -n kern.hv_support 2>/dev/null)" = "1" ] || return 1 |
| [ "$HOST" = "$ARCH_NAME" ] || |
| ( [ "$HOST" = x86_64 ] && [ "$ARCH" = i386 ] ) |
| } |
| |
| set_qemu_accelerator () |
| { |
| # Shellcheck does not see ACCEL_PROPS is used |
| # shellcheck disable=SC2034 |
| ACCEL_PROPS=${ACCEL#"${ACCEL%%,*}"} |
| ACCEL=${ACCEL%%,*} |
| |
| if [ "$ACCEL" = "kvm" ] && ! kvm_available; then |
| echo "KVM is needed, but not available on this host" >&2 |
| return 2 |
| fi |
| if [ "$ACCEL" = "hvf" ] && ! hvf_available; then |
| echo "HVF is needed, but not available on this host" >&2 |
| return 2 |
| fi |
| |
| if [ -z "$ACCEL" ]; then |
| if kvm_available; then |
| ACCEL="kvm" |
| elif hvf_available; then |
| ACCEL="hvf" |
| else |
| ACCEL="tcg" |
| fi |
| fi |
| |
| return 0 |
| } |