diff --git a/scripts/start_vm b/scripts/start_vm
index 42899df9..b081e973 100755
--- a/scripts/start_vm
+++ b/scripts/start_vm
@@ -1,156 +1,15 @@
-#!/bin/sh
+#!/usr/bin/env python3
 #
-# This software is a part of ISAR.
-# Copyright (C) 2015-2017 ilbers GmbH
+# Helper script to start QEMU with Isar image
+# Copyright (c) 2024, ilbers GmbH
 
-set -e
+import sys, os
+sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'testsuite'))
 
-ES_OK=0
-ES_BUG=3
+from start_vm import parse_args, start_qemu
 
-# Convert bitbake assignments to shell ones
-# a = b         a=b
-# a ?= b        a=b
-# TODO: Use bitbake to parse vars correctly (overriding in local.conf, etc.)
-bb2sh() {
-    sed 's/[[:space:]]*?*=[[:space:]]*/=/'
-}
+if __name__ == "__main__":
+    args = parse_args()
 
-start_qemu() {
-    root=`echo $QEMU_DISK_ARGS \
-        | sed 's,##ROOTFS_IMAGE##,'$IMAGE_DIR/$ROOTFS_IMAGE','`
-    [ -n "$KARGS$EXTRA_KARGS" ] && OPT_KERNEL_ARGS="-append $KARGS$EXTRA_KARGS"
-    local had_x
-    echo $- | grep -q x && had_x=1 || had_x=0
-    [ $had_x -eq 0 ] && set -x
-    qemu-system-$QEMU_ARCH \
-	-m 1024M \
-	-M $QEMU_MACHINE \
-	$QCPU \
-	-nographic \
-	$EXTRA_ARGS \
-    $OPT_KERNEL_ARGS \
-	$root
-    [ $had_x -eq 0 ] && set +x
-}
-
-show_help() {
-    echo "This script runs ISAR image in QEMU emulator."
-    echo
-    echo "Usage:"
-    echo "    $0 [params] [BUILD_DIR]"
-    echo "BUILD_DIR is your ISAR build folder. If not set, current folder"
-    echo "is used."
-    echo
-    echo "Parameters:"
-    echo "    -a, --arch ARCH       set isar machine architecture."
-    echo "                          Supported: arm, i386, amd64, arm64, mipsel, riscv64."
-    echo "    -b, --build BUILD     set path to build directory."
-    echo "    -d, --distro DISTRO   set isar Debian distribution."
-    echo "                          Supported: buster, bullseye, bookworm"
-    echo "    -o, --out FILE        Route QEMU console output to"
-    echo "                          specified file."
-    echo "    -p, --pid FILE        Store QEMU pid to file."
-    echo "    -s, --secureboot      Enable secureboot with default MS keys."
-    echo "    --help                display this message and exit."
-    echo
-    echo "Exit status:"
-    echo " 0  if OK,"
-    echo " 3  if invalid parameters are passed."
-}
-
-# Set default values, that can be overwritten from command line
-ARCH=arm
-DISTRO=bookworm
-BUILD_DIR=$PWD
-
-# Parse command line to get user configuration
-while [ $# -gt 0 ]
-do
-    key="$1"
-
-    case $key in
-    -h|--help)
-        show_help
-        exit 0
-        ;;
-    -a|--arch)
-        ARCH=$2
-        shift
-        ;;
-    -b|--build)
-        BUILD_DIR=$2
-        shift
-        ;;
-    -d|--distro)
-        DISTRO=$2
-        shift
-        ;;
-    -o|--out)
-        EXTRA_ARGS="$EXTRA_ARGS -serial file:$2"
-        shift
-        ;;
-    -p|--pid)
-        EXTRA_ARGS="$EXTRA_ARGS -pidfile $2"
-        shift
-        ;;
-    -s|--secureboot)
-        OVMF_VARS_ORIG="/usr/share/OVMF/OVMF_VARS_4M.ms.fd"
-        OVMF_VARS="$(basename "${OVMF_VARS_ORIG}")"
-        cp "${OVMF_VARS_ORIG}" "${OVMF_VARS}"
-        EXTRA_ARGS="$EXTRA_ARGS -drive if=pflash,format=raw,unit=1,file=${OVMF_VARS}"
-        ;;
-    *)
-        echo "error: invalid parameter '$key', please try '--help' to get list of supported parameters"
-        exit $ES_BUG
-        ;;
-    esac
-
-    shift
-done
-
-eval $(bitbake -e mc:qemu$ARCH-$DISTRO:isar-image-base | grep "^DEPLOY_DIR_IMAGE=")
-readonly IMAGE_DIR=$DEPLOY_DIR_IMAGE
-
-readonly ISARROOT="$(dirname "$0")"/..
-
-readonly MACHINE_CONF=$ISARROOT/meta-isar/conf/machine/qemu$ARCH.conf
-eval "$(egrep 'MACHINE_SERIAL|QEMU_' $MACHINE_CONF |bb2sh)"
-
-eval $(bitbake -e mc:qemu$ARCH-$DISTRO:isar-image-base | grep "\(^IMAGE_FSTYPES=\|^IMAGE_FULLNAME=\)")
-# Take first image type for VM if there are several defined
-IMAGE_FSTYPES=$(echo "$IMAGE_FSTYPES" | awk '{print $1}')
-case "$IMAGE_FSTYPES" in
-    ext4)
-    readonly ROOTFS_IMAGE=$IMAGE_FULLNAME.ext4
-
-    eval $(bitbake -e mc:qemu$ARCH-$DISTRO:isar-image-base | grep "^KERNEL_IMAGE=")
-    eval $(bitbake -e mc:qemu$ARCH-$DISTRO:isar-image-base | grep "^INITRD_DEPLOY_FILE=")
-    QKERNEL=$IMAGE_DIR/${KERNEL_IMAGE}
-    QINITRD=/dev/null
-    [ -n "$INITRD_DEPLOY_FILE" ] && QINITRD=$IMAGE_DIR/${INITRD_DEPLOY_FILE}
-    if [ "$ARCH" = "riscv64" ]; then
-        EXTRA_ARGS="$EXTRA_ARGS -device loader,file=$QKERNEL,addr=0x80200000"
-        QKERNEL="/usr/lib/riscv64-linux-gnu/opensbi/qemu/virt/fw_jump.elf"
-    fi
-    EXTRA_ARGS="$EXTRA_ARGS \
-	-kernel $QKERNEL \
-	-initrd $QINITRD"
-    KARGS="console=$MACHINE_SERIAL root=/dev/$QEMU_ROOTFS_DEV rw $QEMU_KARGS"
-    ;;
-    wic)
-    readonly ROOTFS_IMAGE=$IMAGE_FULLNAME.wic
-    EXTRA_ARGS="$EXTRA_ARGS -snapshot"
-    ;;
-    *)
-    echo "IMAGE_FSTYPES \"$IMAGE_FSTYPES\" not supported"
-    exit 1
-    ;;
-esac
-
-QCPU=
-[ -n "$QEMU_CPU" ] && QCPU="-cpu $QEMU_CPU"
-
-start_qemu
-
-exit $ES_OK
+    start_qemu(args.arch, args.build, args.distro, args.image, args.out,
+               args.pid, args.pcbios)
diff --git a/testsuite/README.md b/testsuite/README.md
index 913767fc..cfcfb1bf 100644
--- a/testsuite/README.md
+++ b/testsuite/README.md
@@ -57,11 +57,11 @@ $ avocado run ../testsuite/citest.py -t startvm,full
 
 ## Manual running
 
-There is a tool `start_vm.py` which is the replacement for the bash script in
+There is a tool start_vm which is the replacement for the bash script in
 `isar/scripts` directory. It can be used to run image previously built:
 
 ```
-./start_vm.py -a amd64 -b /build -d bullseye -i isar-image-base
+start_vm -a amd64 -b /build -d bullseye -i isar-image-base
 ```
 
 # Tests for running commands under qemu images
diff --git a/testsuite/cibuilder.py b/testsuite/cibuilder.py
index 227d0a96..12c48180 100755
--- a/testsuite/cibuilder.py
+++ b/testsuite/cibuilder.py
@@ -529,7 +529,7 @@ BBPATH .= ":${LAYERDIR}"\
                                 'DISTRO', \
                                 target=multiconfig)
 
-        # only the first type will be tested in start_vm.py
+        # only the first type will be tested in start_vm
         if image_fstypes.split()[0] == 'wic':
             if wks_file:
                 # ubuntu is less verbose so we do not see the message
diff --git a/testsuite/start_vm.py b/testsuite/start_vm.py
index ef0dfbcb..2c07b816 100755
--- a/testsuite/start_vm.py
+++ b/testsuite/start_vm.py
@@ -98,7 +98,7 @@ def start_qemu(arch, build, distro, image, out, pid, enforce_pcbios):
     print(cmdline)
     p1 = subprocess.call('exec ' + ' '.join(cmdline), shell=True)
 
-if __name__ == "__main__":
+def parse_args():
     parser = argparse.ArgumentParser()
     parser.add_argument('-a', '--arch', choices=['arm', 'arm64', 'amd64', 'i386', 'mipsel'], help='set isar machine architecture.', default='arm')
     parser.add_argument('-b', '--build', help='set path to build directory.', default=os.getcwd())
@@ -107,6 +107,9 @@ if __name__ == "__main__":
     parser.add_argument('-o', '--out', help='Route QEMU console output to specified file.')
     parser.add_argument('-p', '--pid', help='Store QEMU pid to specified file.')
     parser.add_argument('--pcbios', action="store_true", help='remove any bios options to enforce use of pc bios')
-    args = parser.parse_args()
+    return parser.parse_args()
+
+if __name__ == "__main__":
+    args = parse_args()
 
     start_qemu(args.arch, args.build, args.distro, args.image, args.out, args.pid, args.pcbios)
