Skip to content

Commit 01d7592

Browse files
committed
boot-qemu.py: Add support for passing through a folder to the guest
virtiofs, available in QEMU 5.2 or newer and Linux guests 5.4 or newer, is a more modern way to pass local folders along to QEMU, as it takes advantage of the fact that the folders are on the same machine as the hypervisor. To use virtiofs, we first need to run virtiofsd, which is included with most base QEMU packages. Once we find it, we run it in the background and connect to it using some QEMU parameters, which were shamelessly taken from the official virtiofs website: https://virtio-fs.gitlab.io/howto-qemu.html To use it within the guest (you can use a different path than /mnt/shared but 'mount -t virtio shared' must be used): # mkdir /mnt/shared # mount -t virtiofs shared /mnt/shared # echo "$(uname -a)" >/mnt/shared/foo On the host: $ cat shared/foo Linux (none) 6.1.0-rc8-next-20221207 #2 SMP PREEMPT Wed Dec 7 14:56:03 MST 2022 aarch64 GNU/Linux This does require guest kernel support (CONFIG_VIRTIO_FS=y), otherwise it will not work inside the guest: / # mount -t virtiofs shared /mnt/shared mount: mounting shared on /mnt/shared failed: No such device Link: #81 Signed-off-by: Nathan Chancellor <nathan@kernel.org>
1 parent f3173cb commit 01d7592

2 files changed

Lines changed: 88 additions & 8 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
qemu-binaries/
22
*.pyc
3+
shared/
4+
.vfsd.*

boot-qemu.py

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
# pylint: disable=invalid-name
33

44
import argparse
5+
import contextlib
6+
import grp
57
import os
68
from pathlib import Path
79
import platform
@@ -13,6 +15,7 @@
1315
import utils
1416

1517
base_folder = Path(__file__).resolve().parent
18+
shared_folder = base_folder.joinpath('shared')
1619
supported_architectures = [
1720
"arm", "arm32_v5", "arm32_v6", "arm32_v7", "arm64", "arm64be", "m68k",
1821
"mips", "mipsel", "ppc32", "ppc32_mac", "ppc64", "ppc64le", "riscv",
@@ -83,6 +86,12 @@ def parse_arguments():
8386
help= # noqa: E251
8487
"Number of processors for virtual machine. By default, only machines spawned with KVM will use multiple vCPUS."
8588
)
89+
parser.add_argument(
90+
"--share-folder",
91+
action='store_true',
92+
help= # noqa: E251
93+
f"Share {shared_folder} with the guest using virtiofs (requires interactive, not supported with gdb)."
94+
)
8695
parser.add_argument(
8796
"-t",
8897
"--timeout",
@@ -221,6 +230,7 @@ def setup_cfg(args):
221230
* interactive: Whether or not the user is going to be running the
222231
machine interactively.
223232
* kernel_location: The full path to the kernel image or build folder.
233+
* share_folder_with_guest: Share a folder on the host with a guest.
224234
* smp_requested: Whether or not the user specified a value with
225235
'--smp'.
226236
* smp_value: The value to use with '-smp' (will be used when
@@ -246,6 +256,7 @@ def setup_cfg(args):
246256
"gdb": args.gdb,
247257
"gdb_bin": args.gdb_bin,
248258
"interactive": args.interactive or args.gdb,
259+
"share_folder_with_guest": args.share_folder,
249260
"smp_requested": args.smp is not None,
250261
"smp_value": get_smp_value(args),
251262
"timeout": args.timeout,
@@ -733,8 +744,58 @@ def launch_qemu(cfg):
733744
gdb_bin = cfg["gdb_bin"]
734745
kernel_location = cfg["kernel_location"]
735746
qemu_cmd = cfg["qemu_cmd"]
747+
share_folder_with_guest = cfg["share_folder_with_guest"]
736748
timeout = cfg["timeout"]
737749

750+
if share_folder_with_guest and not interactive:
751+
utils.yellow(
752+
'Shared folder requested without an interactive session, ignoring...'
753+
)
754+
share_folder_with_guest = False
755+
if share_folder_with_guest and gdb:
756+
utils.yellow(
757+
'Shared folder requested during a debugging session, ignoring...')
758+
share_folder_with_guest = False
759+
760+
if share_folder_with_guest:
761+
shared_folder.mkdir(exist_ok=True, parents=True)
762+
763+
# If shared folder was requested, we need to search for virtiofsd in
764+
# certain known locations.
765+
qemu_prefix = Path(qemu_cmd[0]).resolve().parent.parent
766+
virtiofsd_locations = [
767+
Path('libexec', 'virtiofsd'), # Default QEMU installation, Fedora
768+
Path('lib', 'qemu', 'virtiofsd'), # Arch Linux, Debian, Ubuntu
769+
]
770+
virtiofsd = utils.find_first_file(qemu_prefix, virtiofsd_locations)
771+
772+
if not (sudo := shutil.which('sudo')):
773+
raise Exception(
774+
'sudo is required to use virtiofsd but it could not be found!')
775+
utils.green(
776+
'Requesting sudo permission to run virtiofsd in the background...')
777+
subprocess.run([sudo, 'true'], check=True)
778+
779+
virtiofsd_log = base_folder.joinpath('.vfsd.log')
780+
virtiofsd_mem = base_folder.joinpath('.vfsd.mem')
781+
virtiofsd_socket = base_folder.joinpath('.vfsd.sock')
782+
virtiofsd_cmd = [
783+
sudo,
784+
virtiofsd,
785+
f"--socket-group={grp.getgrgid(os.getgid()).gr_name}",
786+
f"--socket-path={virtiofsd_socket}",
787+
'-o', f"source={shared_folder}",
788+
'-o', 'cache=always',
789+
] # yapf: disable
790+
791+
qemu_mem = qemu_cmd[qemu_cmd.index('-m') + 1]
792+
qemu_cmd += [
793+
'-chardev', f"socket,id=char0,path={virtiofsd_socket}",
794+
'-device', 'vhost-user-fs-pci,queue-size=1024,chardev=char0,tag=shared',
795+
'-object', f"memory-backend-file,id=shm,mem-path={virtiofsd_mem},share=on,size={qemu_mem}",
796+
'-numa', 'node,memdev=shm',
797+
] # yapf: disable
798+
738799
# Print information about the QEMU binary
739800
pretty_print_qemu_info(qemu_cmd[0])
740801

@@ -772,14 +833,31 @@ def launch_qemu(cfg):
772833
qemu_cmd = timeout_cmd + stdbuf_cmd + qemu_cmd
773834

774835
pretty_print_qemu_cmd(qemu_cmd)
775-
try:
776-
subprocess.run(qemu_cmd, check=True)
777-
except subprocess.CalledProcessError as ex:
778-
if ex.returncode == 124:
779-
utils.red("ERROR: QEMU timed out!")
780-
else:
781-
utils.red("ERROR: QEMU did not exit cleanly!")
782-
sys.exit(ex.returncode)
836+
null_cm = contextlib.nullcontext()
837+
with open(virtiofsd_log, 'w', encoding='utf-8') if share_folder_with_guest else null_cm as vfsd_log, \
838+
subprocess.Popen(virtiofsd_cmd, stderr=vfsd_log, stdout=vfsd_log) if share_folder_with_guest else null_cm as vfsd_process:
839+
try:
840+
subprocess.run(qemu_cmd, check=True)
841+
except subprocess.CalledProcessError as ex:
842+
if ex.returncode == 124:
843+
utils.red("ERROR: QEMU timed out!")
844+
else:
845+
utils.red("ERROR: QEMU did not exit cleanly!")
846+
# If virtiofsd is dead, it is pretty likely that it was the
847+
# cause of QEMU failing so add to the existing exception using
848+
# 'from'.
849+
if vfsd_process and vfsd_process.poll():
850+
vfsd_log_txt = virtiofsd_log.read_text(
851+
encoding='utf-8')
852+
raise Exception(
853+
f"virtiofsd failed with: {vfsd_log_txt}") from ex
854+
sys.exit(ex.returncode)
855+
finally:
856+
if vfsd_process:
857+
vfsd_process.kill()
858+
# Delete the memory to save space, it does not have to be
859+
# persistent
860+
virtiofsd_mem.unlink(missing_ok=True)
783861

784862

785863
if __name__ == '__main__':

0 commit comments

Comments
 (0)