Qemu support for the QXL driver and SPICE (#481)

* pmb.helpers.run: support running processes in background
* enable QXL driver support in the linux kernel configurations so
that we can also use SPICE to connect to the VM.

QXL is a paravirtual graphics driver with 2D support

The SPICE project aims to provide a complete open source solution for remote
access to virtual machines in a seamless way.

Both DRM_QXL and DRM_BOCHS are enabled as modules.
According to [1], on Linux guests, the qxl and bochs_drm kernel modules
must be loaded in order to gain a decent performance

* qemu: add new option --spice to connect to VM using a SPICE client

If specified, 'pmbootstrap qemu' will look for some SPICE client in the
user's PATH and run qemu using the QXL driver.

Currently supported spice clients are 'spicy' and 'remote-viewer' but
adding support for more clients can be easily done.

qemu with qxl support will run on port 8077/tcp, which doesn't belong to
any well-known service and represents 'PM' in decimal.

References:
[0] https://www.linux-kvm.org/page/SPICE
[1] https://wiki.archlinux.org/index.php/QEMU#qxl
[2] https://wiki.archlinux.org/index.php/QEMU#SPICE
[3] https://github.com/postmarketOS/pmbootstrap/issues/453 (partially fixed)
This commit is contained in:
Pablo Castellano 2017-09-26 22:52:00 +02:00 committed by Oliver Smith
parent d48ca092b4
commit c855ee095b
13 changed files with 137 additions and 49 deletions

View File

@ -1,6 +1,6 @@
pkgname=device-qemu-aarch64
pkgver=1
pkgrel=2
pkgrel=3
pkgdesc="Simulated device in qemu with vexpress soc"
url="https://github.com/postmarketOS"
arch="noarch"
@ -22,5 +22,5 @@ package() {
"$pkgdir"/etc/network/interfaces
}
sha512sums="56362f2482e94349c8ba821245dfbdec6e6ebc0ed47786ae333791faa8840fe6ef8d5a2f44b599dcfcbba499ed275546ebc2540428765e6eba2f146ba46795f5 deviceinfo
sha512sums="692f5e0b5e8f10a88f5c81631377981eb9bdd22cce2e288f7849c67488f6d2b60ba6919db3c99907238c33507a58948e95fe5c4fd262fb2688da6060cd7129d9 deviceinfo
d510ca304066840aa5e6c4fc71ded1b7e83012c93837fa39e37bdb873b3636230030d56f7aa50c93fc688f563cb4cb96c02ad333bbc45a400c1ebee1792a9dd4 interfaces"

View File

@ -8,7 +8,7 @@ deviceinfo_date=""
deviceinfo_keyboard="true"
deviceinfo_nonfree="????"
deviceinfo_dtb=""
deviceinfo_modules_initfs="virtio-gpu virtio_net"
deviceinfo_modules_initfs="virtio-gpu virtio_net qxl drm_bochs"
deviceinfo_external_disk="true"
deviceinfo_external_disk_install="true"
deviceinfo_flash_methods="none"

View File

@ -1,6 +1,6 @@
pkgname=device-qemu-amd64
pkgver=1
pkgrel=3
pkgrel=4
pkgdesc="Simulated device in qemu with an x86 platform"
url="https://github.com/postmarketOS"
arch="noarch"
@ -22,5 +22,5 @@ package() {
"$pkgdir"/etc/network/interfaces
}
sha512sums="a02b2c46f913b148663b1252f71210a6ae1afa34af347664eac70570e9580b11257d23edfaa78797d03187e3bc3248c09c483e6c93ec0f0476d8b74af97e00d0 deviceinfo
sha512sums="511fe8d2f1e4cf90d96a321568220ecf98bffd44b96339b200633a4f3c45b6b58e646c0971f82fbcd9829e4b66e62942a6ecc55ea03dd228ab76984b0ff0b92d deviceinfo
d510ca304066840aa5e6c4fc71ded1b7e83012c93837fa39e37bdb873b3636230030d56f7aa50c93fc688f563cb4cb96c02ad333bbc45a400c1ebee1792a9dd4 interfaces"

View File

@ -8,7 +8,7 @@ deviceinfo_date=""
deviceinfo_keyboard="true"
deviceinfo_nonfree="????"
deviceinfo_dtb=""
deviceinfo_modules_initfs=""
deviceinfo_modules_initfs="qxl drm_bochs"
deviceinfo_external_disk="true"
deviceinfo_external_disk_install="true"
deviceinfo_flash_methods="none"

View File

@ -1,6 +1,6 @@
pkgname=device-qemu-vexpress
pkgver=1
pkgrel=9
pkgrel=10
pkgdesc="Simulated device in qemu with vexpress soc"
url="https://github.com/postmarketOS"
arch="noarch"
@ -22,5 +22,5 @@ package() {
"$pkgdir"/etc/network/interfaces
}
sha512sums="a18ab789c19d8802be6a9e9c842f4c304f267bd78f46b096b4ba6609430d1544e0a9eca9bc22db67a0728efae3aad6c2def9f7e64157d31aeb31b4bb839076d5 deviceinfo
sha512sums="4d49aca03d0c87ba1d35056c0985946bfeb9240130014f7e3b7155c38e4bfcdae8a67747969d1254fe064fe0b71847c537ca1e07bc5fd4173c6387270d72c4f2 deviceinfo
d510ca304066840aa5e6c4fc71ded1b7e83012c93837fa39e37bdb873b3636230030d56f7aa50c93fc688f563cb4cb96c02ad333bbc45a400c1ebee1792a9dd4 interfaces"

View File

@ -8,7 +8,7 @@ deviceinfo_date=""
deviceinfo_keyboard="true"
deviceinfo_nonfree="????"
deviceinfo_dtb="vexpress-v2p-ca9"
deviceinfo_modules_initfs="amba-clcd virtio_net virtio_mmio ambakmi libps2 pcips2"
deviceinfo_modules_initfs="amba-clcd virtio_net virtio_mmio ambakmi libps2 pcips2 qxl drm_bochs"
deviceinfo_external_disk="true"
deviceinfo_external_disk_install="true"
deviceinfo_flash_methods="none"

View File

@ -18,7 +18,7 @@ case $pkgver in
*.*.*) _kernver=${pkgver%.*};;
*.*) _kernver=$pkgver;;
esac
pkgrel=2
pkgrel=3
arch="all"
pkgdesc="Mainline Linux for pmOS supported chipsets (OMAP)"
@ -189,8 +189,8 @@ dev() {
}
sha512sums="a557c2f0303ae618910b7106ff63d9978afddf470f03cb72aa748213e099a0ecd5f3119aea6cbd7b61df30ca6ef3ec57044d524b7babbaabddf8b08b8bafa7d2 linux-4.13.tar.xz
03407ddd93db7188928ff7f689dff01afaa0119655e311440a8a286907d8f27bf058487b8f859a588f7f06bda98abe24998fc580f917df2f96f9b661905a54ce config-postmarketos.armhf
d41363bc6b9878db95b44eb326f9fd0ea89b6f3364ff0e883a5142cfa2937e6b77e9b185c046cbd2c5d4e2f696349839836740f8dee6bd649ef995cbd756e08a config-postmarketos.aarch64
8d3ea9743190680036d938a9d8ce8602f453152c69404741c70f1584075c111ba120d986b712fc509bbbe7882204292527738de9f03cf871c4911fff80a58d13 config-postmarketos.x86_64
ab2b16db1aa1bb7550ac0c98215b549ca5f03db80b226effe05121f8175520f3a3accc4fb2b42c7ba1757282e77b844e11d8350d1a83e23821ae4e70da87996c config-postmarketos.armhf
d49d5d01adb7f314d5649cf8d7e917eeca9ec286eff2687c6865a7fac46e18227e278ca8965ad29bd99320a676e167256dc1b4212c5702f819e4cd2cf40c33c9 config-postmarketos.aarch64
4a95d0ddbf7023b41a3d438d439951b9878ce92194d3576c0ef3c1f1f8734331728c2aa03e736908d31bfcfd7c03b2760984714beb39573082916bc50c93c199 config-postmarketos.x86_64
17c48bb7b4218297bd2be6faa5b6570ce1560a33385237a9962c0884d782c9a722a25a30077b6721d2943a9b98c29dcad6adfef718b0163c559c19a79519319b 0001-rx51-drm-regression-workaround.patch
7c0675386c0906178661313d2dbaf644df9b43af31c4b8c8cc840c59b952c04c5768089782d79d84fd363e26b1824e05d1516a80b8cae663225fcb9b252d848a patch-4.13.3.xz"

View File

@ -3412,7 +3412,7 @@ CONFIG_DRM_EXYNOS_MIC=y
# CONFIG_DRM_RCAR_DU is not set
# CONFIG_DRM_RCAR_DW_HDMI is not set
CONFIG_DRM_QXL=m
# CONFIG_DRM_BOCHS is not set
CONFIG_DRM_BOCHS=m
CONFIG_DRM_VIRTIO_GPU=m
CONFIG_DRM_MSM=m
# CONFIG_DRM_MSM_REGISTER_LOGGING is not set

View File

@ -2966,6 +2966,7 @@ CONFIG_DRM_KMS_FB_HELPER=y
CONFIG_DRM_FBDEV_EMULATION=y
CONFIG_DRM_FBDEV_OVERALLOC=100
# CONFIG_DRM_LOAD_EDID_FIRMWARE is not set
CONFIG_DRM_TTM=m
CONFIG_DRM_GEM_CMA_HELPER=y
CONFIG_DRM_KMS_CMA_HELPER=y
@ -3027,8 +3028,8 @@ CONFIG_DRM_OMAP_PANEL_SONY_ACX565AKM=y
# CONFIG_DRM_OMAP_PANEL_TPO_TD043MTEA1 is not set
# CONFIG_DRM_OMAP_PANEL_NEC_NL8048HL11 is not set
# CONFIG_DRM_TILCDC is not set
# CONFIG_DRM_QXL is not set
# CONFIG_DRM_BOCHS is not set
CONFIG_DRM_QXL=m
CONFIG_DRM_BOCHS=m
# CONFIG_DRM_VIRTIO_GPU is not set
# CONFIG_DRM_FSL_DCU is not set
# CONFIG_DRM_STM is not set

View File

@ -3178,8 +3178,8 @@ CONFIG_DRM_TTM=y
# CONFIG_DRM_MGAG200 is not set
CONFIG_DRM_CIRRUS_QEMU=y
# CONFIG_DRM_RCAR_DW_HDMI is not set
# CONFIG_DRM_QXL is not set
CONFIG_DRM_BOCHS=y
CONFIG_DRM_QXL=m
CONFIG_DRM_BOCHS=m
# CONFIG_DRM_VIRTIO_GPU is not set
CONFIG_DRM_PANEL=y

View File

@ -22,7 +22,7 @@ import os
def core(args, cmd, log_message, log, return_stdout, check=True,
working_dir=None):
working_dir=None, background=False):
logging.debug(log_message)
"""
Run the command and write the output to the log.
@ -35,29 +35,37 @@ def core(args, cmd, log_message, log, return_stdout, check=True,
os.chdir(working_dir)
ret = None
try:
if log:
if return_stdout:
ret = subprocess.check_output(cmd).decode("utf-8")
args.logfd.write(ret)
else:
subprocess.check_call(cmd, stdout=args.logfd,
stderr=args.logfd)
args.logfd.flush()
else:
logging.debug("*** output passed to pmbootstrap stdout, not" +
" to this log ***")
subprocess.check_call(cmd)
except subprocess.CalledProcessError as exc:
if check:
if log:
logging.debug("^" * 70)
logging.info("NOTE: The failed command's output is above"
" the ^^^ line in the logfile: " + args.log)
raise RuntimeError("Command failed: " + log_message) from exc
if background:
if log:
ret = subprocess.Popen(cmd, stdout=args.logfd, stderr=args.logfd)
else:
pass
ret = subprocess.Popen(cmd)
logging.debug("Started process in background with PID " + str(ret.pid))
else:
try:
if log:
if return_stdout:
ret = subprocess.check_output(cmd).decode("utf-8")
args.logfd.write(ret)
else:
subprocess.check_call(cmd, stdout=args.logfd,
stderr=args.logfd)
args.logfd.flush()
else:
logging.debug("*** output passed to pmbootstrap stdout, not" +
" to this log ***")
subprocess.check_call(cmd)
except subprocess.CalledProcessError as exc:
if check:
if log:
logging.debug("^" * 70)
logging.info("NOTE: The failed command's output is above"
" the ^^^ line in the logfile: " + args.log)
raise RuntimeError("Command failed: " + log_message) from exc
else:
pass
if working_dir:
os.chdir(working_dir_old)
@ -65,7 +73,7 @@ def core(args, cmd, log_message, log, return_stdout, check=True,
def user(args, cmd, log=True, working_dir=None, return_stdout=False,
check=True):
check=True, background=False):
if working_dir:
msg = "% cd " + working_dir + " && " + " ".join(cmd)
@ -73,13 +81,14 @@ def user(args, cmd, log=True, working_dir=None, return_stdout=False,
msg = "% " + " ".join(cmd)
# TODO: maintain and check against a whitelist
return core(args, cmd, msg, log, return_stdout, check, working_dir)
return core(args, cmd, msg, log, return_stdout, check, working_dir,
background)
def root(args, cmd, log=True, working_dir=None, return_stdout=False,
check=True):
check=True, background=False):
"""
:param working_dir: defaults to args.work
"""
cmd = ["sudo"] + cmd
return user(args, cmd, log, working_dir, return_stdout, check)
return user(args, cmd, log, working_dir, return_stdout, check, background)

View File

@ -294,6 +294,11 @@ def arguments():
help="guest RAM (default: 1024)")
qemu.add_argument("-p", "--port", type=int, default=2222,
help="ssh port (default: 2222)")
qemu.add_argument("--spice", dest="use_spice",
default=False, action="store_true",
help="connect to the VM using SPICE (NOTE: you need to"
" have a SPICE client installed in your host"
" machine)")
# Use defaults from the user's config file
args = parser.parse_args()

View File

@ -63,7 +63,49 @@ def which_qemu(args, arch):
" run qemu.")
def qemu_command(args, arch, device, img_path):
def which_spice(args):
"""
Finds some SPICE executable or raises an exception otherwise
:returns: tuple (spice_was_found, path_to_spice_executable)
"""
executables = ["remote-viewer", "spicy"]
for executable in executables:
if shutil.which(executable):
return executable
return None
def spice_command(args):
"""
Generate the full SPICE command with arguments connect to
the virtual machine
:returns: tuple (dict, list), configuration parameters and spice command
"""
parameters = {
"spice_addr": "127.0.0.1",
"spice_port": "8077"
}
if not args.use_spice:
parameters["enable_spice"] = False
return parameters, []
spice_binary = which_spice(args)
if not spice_binary:
parameters["enable_spice"] = False
return parameters, []
spice_addr = parameters["spice_addr"]
spice_port = parameters["spice_port"]
commands = {
"spicy": ["spicy", "-h", spice_addr, "-p", spice_port],
"remote-viewer": [
"remote-viewer",
"spice://" + spice_addr + "?port=" + spice_port
]
}
parameters["enable_spice"] = True
return parameters, commands[spice_binary]
def qemu_command(args, arch, device, img_path, config):
"""
Generate the full qemu command with arguments to run postmarketOS
"""
@ -132,6 +174,13 @@ def qemu_command(args, arch, device, img_path):
else:
logging.info("Warning: qemu is not using KVM and will run slower!")
# QXL / SPICE (2D acceleration support)
if config["enable_spice"]:
command += ["-vga", "qxl"]
command += ["-spice",
"port={spice_port},addr={spice_addr}".format(**config) +
",disable-ticketing"]
return command
@ -179,18 +228,20 @@ def run(args):
arch = pmb.parse.arch.uname_to_qemu(args.arch_native)
if args.arch:
arch = pmb.parse.arch.uname_to_qemu(args.arch)
logging.info("Running postmarketOS in QEMU VM (" + arch + ")")
device = pmb.parse.arch.qemu_to_pmos_device(arch)
img_path = system_image(args, device)
spice_parameters, command_spice = spice_command(args)
# Workaround: qemu runs as local user and needs write permissions in the
# system image, which is owned by root
if not os.access(img_path, os.W_OK):
pmb.helpers.run.root(args, ["chmod", "666", img_path])
command = qemu_command(args, arch, device, img_path)
run_spice = spice_parameters["enable_spice"]
command = qemu_command(args, arch, device, img_path, spice_parameters)
logging.info("Running postmarketOS in QEMU VM (" + arch + ")")
logging.info("Command: " + " ".join(command))
if args.image_size:
@ -200,9 +251,31 @@ def run(args):
" the system image size when you run out of space!")
print()
logging.info("You can connect to the Virtual Machine using the"
logging.info("You can connect to the virtual machine using the"
" following services:")
logging.info("(ssh) ssh -p " + str(args.port) + " user@localhost")
logging.info("(telnet) telnet localhost " + str(args.port + 1))
logging.info("(telnet debug) telnet localhost " + str(args.port + 2))
pmb.helpers.run.user(args, command)
# SPICE related messages
if not run_spice:
if args.use_spice:
logging.warning("WARNING: Could not find any SPICE client (spicy,"
" remote-viewer) in your PATH, starting without"
" SPICE support!")
else:
logging.info("NOTE: Consider using --spice for potential"
" performance improvements (2d acceleration)")
try:
process = pmb.helpers.run.user(args, command, background=run_spice)
# Launch SPICE client
if run_spice:
logging.info("Command: " + " ".join(command_spice))
pmb.helpers.run.user(args, command_spice)
except KeyboardInterrupt:
pass
finally:
if process:
process.terminate()