summaryrefslogtreecommitdiffstats
path: root/meta-agl-kvm-demo
diff options
context:
space:
mode:
authorScott Murray <scott.murray@konsulko.com>2024-05-12 15:28:59 -0400
committerScott Murray <scott.murray@konsulko.com>2024-05-27 16:03:25 +0000
commit2e0fd28b2470f6d81bfb1b0b273af5742060ed6a (patch)
treebc485361a96ada08b826b02f7141ae5f7565c2f9 /meta-agl-kvm-demo
parentea7e39dc258c5e7d3a46ffb31b3321331ba9e900 (diff)
Rework KVM demo configuration
Changes: - Move KVM demo configuration and image recipes to a new sub-layer, meta-agl-kvm-demo, to keep things that may not be buildable with just the agl-demo feature separate to avoid confusion. It will hopefully also avoid clutter in recipes-platform/images. This sub-layer is pulled in when the agl-kvm setup feature is given to aglsetup.sh. - Remove the agl-kvm-host-kuksa and agl-kvm-host-audio setup features. - Add new *-guest and *-guest-preconfigured flavors of the Flutter IVI and IC images under meta-agl-kvm-demo that that have the desired configuration changes baked in. - Add required qemu-config recipe variants for the new guest image flavors. At the moment there is more duplication of configuration for this than is desired, and some reworking of agl-qemu-runner configuration may come as a follow up to avoid this. - Remove qemu-config recipe variants for unused Qt guest images. If it becomes desirable to use the Qt demo images as guests this can be revisited. - Added agl-kvm-demo-flutter-preconfigured image variant that supports the full demo setup (i.e. "green machine" with steering wheel, equivalent to CES 2024 demos). NOTES: - The agl-kvm-demo image remains and builds roughly the same image as before, with the KUKSA.val databroker running in the IVI guest and cluster support enabled. Replacing this image with a bbclass abstraction and an e.g. agl-kvm-demo-flutter image is under consideration. Bug-AGL: SPEC-5138 Change-Id: I64936208fd032e5ba47366e3a7ff572dc18338e4 Signed-off-by: Scott Murray <scott.murray@konsulko.com> Reviewed-on: https://gerrit.automotivelinux.org/gerrit/c/AGL/meta-agl-demo/+/29918 ci-image-build: Jenkins Job builder account Tested-by: Jenkins Job builder account ci-image-boot-test: Jenkins Job builder account
Diffstat (limited to 'meta-agl-kvm-demo')
-rw-r--r--meta-agl-kvm-demo/conf/layer.conf15
-rw-r--r--meta-agl-kvm-demo/recipes-config/qemu-config/files/agl-cluster-demo-flutter-guest-preconfigured.conf6
-rw-r--r--meta-agl-kvm-demo/recipes-config/qemu-config/files/agl-cluster-demo-flutter-guest.conf6
-rw-r--r--meta-agl-kvm-demo/recipes-config/qemu-config/files/agl-ivi-demo-flutter-guest-preconfigured.conf13
-rw-r--r--meta-agl-kvm-demo/recipes-config/qemu-config/files/agl-ivi-demo-flutter-guest.conf13
-rw-r--r--meta-agl-kvm-demo/recipes-config/qemu-config/files/bridge.conf1
-rw-r--r--meta-agl-kvm-demo/recipes-config/qemu-config/files/connman-nodnsproxy.conf3
-rw-r--r--meta-agl-kvm-demo/recipes-config/qemu-config/files/dnsmasq-qemu.conf4
-rw-r--r--meta-agl-kvm-demo/recipes-config/qemu-config/files/vmnet0.netdev3
-rw-r--r--meta-agl-kvm-demo/recipes-config/qemu-config/files/vmnet0.network7
-rw-r--r--meta-agl-kvm-demo/recipes-config/qemu-config/qemu-config-agl-cluster-demo-flutter-guest-preconfigured.bb3
-rw-r--r--meta-agl-kvm-demo/recipes-config/qemu-config/qemu-config-agl-cluster-demo-flutter-guest.bb29
-rw-r--r--meta-agl-kvm-demo/recipes-config/qemu-config/qemu-config-agl-ivi-demo-flutter-guest-preconfigured.bb3
-rw-r--r--meta-agl-kvm-demo/recipes-config/qemu-config/qemu-config-agl-ivi-demo-flutter-guest.bb29
-rw-r--r--meta-agl-kvm-demo/recipes-config/qemu-config/qemu-config-vmnet0.bb38
-rw-r--r--meta-agl-kvm-demo/recipes-extended/agl-qemu-runner/agl-qemu-runner.bb25
-rwxr-xr-xmeta-agl-kvm-demo/recipes-extended/agl-qemu-runner/files/agl-qemu-runner.sh72
-rw-r--r--meta-agl-kvm-demo/recipes-extended/agl-qemu-runner/files/agl-qemu-runner@.service12
-rw-r--r--meta-agl-kvm-demo/recipes-graphics/wayland/weston-ini-conf.bbappend1
-rw-r--r--meta-agl-kvm-demo/recipes-graphics/wayland/weston-ini-conf/weston.ini.kvm.in18
-rw-r--r--meta-agl-kvm-demo/recipes-graphics/wayland/weston-ini-conf_agldemo.inc66
-rw-r--r--meta-agl-kvm-demo/recipes-platform/images/agl-cluster-demo-flutter-guest-preconfigured.bb12
-rw-r--r--meta-agl-kvm-demo/recipes-platform/images/agl-cluster-demo-flutter-guest.bb8
-rw-r--r--meta-agl-kvm-demo/recipes-platform/images/agl-ivi-demo-flutter-guest-preconfigured.bb12
-rw-r--r--meta-agl-kvm-demo/recipes-platform/images/agl-ivi-demo-flutter-guest.bb14
-rw-r--r--meta-agl-kvm-demo/recipes-platform/images/agl-kvm-demo-flutter-preconfigured.bb38
-rw-r--r--meta-agl-kvm-demo/recipes-platform/images/agl-kvm-demo.bb69
27 files changed, 520 insertions, 0 deletions
diff --git a/meta-agl-kvm-demo/conf/layer.conf b/meta-agl-kvm-demo/conf/layer.conf
new file mode 100644
index 00000000..0799d4f4
--- /dev/null
+++ b/meta-agl-kvm-demo/conf/layer.conf
@@ -0,0 +1,15 @@
+# We have a conf and classes directory, add to BBPATH
+BBPATH =. "${LAYERDIR}:"
+
+# We have recipes-* directories, add to BBFILES
+BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \
+ ${LAYERDIR}/recipes-*/*/*.bbappend"
+
+BBFILE_COLLECTIONS += "agl-kvm-demo-layer"
+BBFILE_PATTERN_agl-kvm-demo-layer = "^${LAYERDIR}/"
+BBFILE_PRIORITY_agl-kvm-demo-layer = "70"
+
+LAYERSERIES_COMPAT_agl-kvm-demo-layer = "kirkstone"
+
+LAYERDEPENDS_agl-kvm-demo-layer = "agldemo"
+
diff --git a/meta-agl-kvm-demo/recipes-config/qemu-config/files/agl-cluster-demo-flutter-guest-preconfigured.conf b/meta-agl-kvm-demo/recipes-config/qemu-config/files/agl-cluster-demo-flutter-guest-preconfigured.conf
new file mode 100644
index 00000000..37003abd
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-config/qemu-config/files/agl-cluster-demo-flutter-guest-preconfigured.conf
@@ -0,0 +1,6 @@
+QEMU_TASKSET_CPUS="4-7"
+QEMU_SMP_OPT="-smp 2"
+QEMU_MEM_OPT="-m 1G"
+QEMU_NET_OPT="-netdev bridge,br=vmnet0,id=net0 -device virtio-net-device,mac=52:54:00:12:00:03,netdev=net0"
+QEMU_KERNEL_CMDLINE_APPEND="root=/dev/vda rw mem=2048M video=Virtual-1:1920x1080"
+QEMU_XDG_APP_ID="agl-cluster-demo"
diff --git a/meta-agl-kvm-demo/recipes-config/qemu-config/files/agl-cluster-demo-flutter-guest.conf b/meta-agl-kvm-demo/recipes-config/qemu-config/files/agl-cluster-demo-flutter-guest.conf
new file mode 100644
index 00000000..37003abd
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-config/qemu-config/files/agl-cluster-demo-flutter-guest.conf
@@ -0,0 +1,6 @@
+QEMU_TASKSET_CPUS="4-7"
+QEMU_SMP_OPT="-smp 2"
+QEMU_MEM_OPT="-m 1G"
+QEMU_NET_OPT="-netdev bridge,br=vmnet0,id=net0 -device virtio-net-device,mac=52:54:00:12:00:03,netdev=net0"
+QEMU_KERNEL_CMDLINE_APPEND="root=/dev/vda rw mem=2048M video=Virtual-1:1920x1080"
+QEMU_XDG_APP_ID="agl-cluster-demo"
diff --git a/meta-agl-kvm-demo/recipes-config/qemu-config/files/agl-ivi-demo-flutter-guest-preconfigured.conf b/meta-agl-kvm-demo/recipes-config/qemu-config/files/agl-ivi-demo-flutter-guest-preconfigured.conf
new file mode 100644
index 00000000..c2d420eb
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-config/qemu-config/files/agl-ivi-demo-flutter-guest-preconfigured.conf
@@ -0,0 +1,13 @@
+QEMU_TASKSET_CPUS="0-3"
+QEMU_SMP_OPT="-smp 4"
+QEMU_MEM_OPT="-m 4G"
+# With touchscreen plugged into the bottom connector of the 2x USB 2.0
+# on the reference hardware
+QEMU_INPUT_OPT="-device qemu-xhci -usb -device usb-host,hostbus=5,hostport=1"
+# Fallback for touchscreen or other pointing device plugged in elsewhere
+#QEMU_INPUT_OPT="-device virtio-tablet-device"
+QEMU_AUDIO_OPT="-audiodev alsa,id=agl -device intel-hda -device hda-duplex,audiodev=agl"
+QEMU_NET_OPT="-netdev bridge,br=vmnet0,id=net0 -device virtio-net-device,mac=52:54:00:12:00:02,netdev=net0"
+QEMU_CAN_OPT="-object can-bus,id=canbus0 -object can-host-socketcan,id=canhost0,if=can0,canbus=canbus0 -device kvaser_pci,canbus=canbus0"
+QEMU_KERNEL_CMDLINE_APPEND="root=/dev/vda rw mem=4196M video=Virtual-1:1920x1080"
+QEMU_XDG_APP_ID="agl-ivi-demo"
diff --git a/meta-agl-kvm-demo/recipes-config/qemu-config/files/agl-ivi-demo-flutter-guest.conf b/meta-agl-kvm-demo/recipes-config/qemu-config/files/agl-ivi-demo-flutter-guest.conf
new file mode 100644
index 00000000..c2d420eb
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-config/qemu-config/files/agl-ivi-demo-flutter-guest.conf
@@ -0,0 +1,13 @@
+QEMU_TASKSET_CPUS="0-3"
+QEMU_SMP_OPT="-smp 4"
+QEMU_MEM_OPT="-m 4G"
+# With touchscreen plugged into the bottom connector of the 2x USB 2.0
+# on the reference hardware
+QEMU_INPUT_OPT="-device qemu-xhci -usb -device usb-host,hostbus=5,hostport=1"
+# Fallback for touchscreen or other pointing device plugged in elsewhere
+#QEMU_INPUT_OPT="-device virtio-tablet-device"
+QEMU_AUDIO_OPT="-audiodev alsa,id=agl -device intel-hda -device hda-duplex,audiodev=agl"
+QEMU_NET_OPT="-netdev bridge,br=vmnet0,id=net0 -device virtio-net-device,mac=52:54:00:12:00:02,netdev=net0"
+QEMU_CAN_OPT="-object can-bus,id=canbus0 -object can-host-socketcan,id=canhost0,if=can0,canbus=canbus0 -device kvaser_pci,canbus=canbus0"
+QEMU_KERNEL_CMDLINE_APPEND="root=/dev/vda rw mem=4196M video=Virtual-1:1920x1080"
+QEMU_XDG_APP_ID="agl-ivi-demo"
diff --git a/meta-agl-kvm-demo/recipes-config/qemu-config/files/bridge.conf b/meta-agl-kvm-demo/recipes-config/qemu-config/files/bridge.conf
new file mode 100644
index 00000000..72c73f39
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-config/qemu-config/files/bridge.conf
@@ -0,0 +1 @@
+allow vmnet0
diff --git a/meta-agl-kvm-demo/recipes-config/qemu-config/files/connman-nodnsproxy.conf b/meta-agl-kvm-demo/recipes-config/qemu-config/files/connman-nodnsproxy.conf
new file mode 100644
index 00000000..9d7f74b8
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-config/qemu-config/files/connman-nodnsproxy.conf
@@ -0,0 +1,3 @@
+[Service]
+ExecStart=
+ExecStart=/usr/sbin/connmand -n --nodnsproxy
diff --git a/meta-agl-kvm-demo/recipes-config/qemu-config/files/dnsmasq-qemu.conf b/meta-agl-kvm-demo/recipes-config/qemu-config/files/dnsmasq-qemu.conf
new file mode 100644
index 00000000..4ab6ee3b
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-config/qemu-config/files/dnsmasq-qemu.conf
@@ -0,0 +1,4 @@
+interface=vmnet0
+dhcp-range=172.16.10.1,172.16.10.5,255.255.255.0,12h
+dhcp-host=52:54:00:12:00:02,172.16.10.2
+dhcp-host=52:54:00:12:00:03,172.16.10.3
diff --git a/meta-agl-kvm-demo/recipes-config/qemu-config/files/vmnet0.netdev b/meta-agl-kvm-demo/recipes-config/qemu-config/files/vmnet0.netdev
new file mode 100644
index 00000000..e2545317
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-config/qemu-config/files/vmnet0.netdev
@@ -0,0 +1,3 @@
+[NetDev]
+Name=vmnet0
+Kind=bridge
diff --git a/meta-agl-kvm-demo/recipes-config/qemu-config/files/vmnet0.network b/meta-agl-kvm-demo/recipes-config/qemu-config/files/vmnet0.network
new file mode 100644
index 00000000..600f8ac0
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-config/qemu-config/files/vmnet0.network
@@ -0,0 +1,7 @@
+[Match]
+Name=vmnet0
+
+[Network]
+Address=172.16.10.1/24
+IPForward=yes
+IPMasquerade=yes
diff --git a/meta-agl-kvm-demo/recipes-config/qemu-config/qemu-config-agl-cluster-demo-flutter-guest-preconfigured.bb b/meta-agl-kvm-demo/recipes-config/qemu-config/qemu-config-agl-cluster-demo-flutter-guest-preconfigured.bb
new file mode 100644
index 00000000..b58dcae6
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-config/qemu-config/qemu-config-agl-cluster-demo-flutter-guest-preconfigured.bb
@@ -0,0 +1,3 @@
+require qemu-config-agl-cluster-demo-flutter-guest.bb
+
+QEMU_IMAGE = "agl-cluster-demo-flutter-guest-preconfigured"
diff --git a/meta-agl-kvm-demo/recipes-config/qemu-config/qemu-config-agl-cluster-demo-flutter-guest.bb b/meta-agl-kvm-demo/recipes-config/qemu-config/qemu-config-agl-cluster-demo-flutter-guest.bb
new file mode 100644
index 00000000..96d95113
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-config/qemu-config/qemu-config-agl-cluster-demo-flutter-guest.bb
@@ -0,0 +1,29 @@
+SUMMARY = "Setting files for agl-cluster-demo-plaform-flutter guest VM"
+LICENSE = "MIT"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
+
+inherit systemd allarch
+
+SRC_URI = "file://${QEMU_IMAGE}.conf"
+
+do_configure[noexec] = "1"
+do_compile[noexec] = "1"
+
+QEMU_IMAGE = "agl-cluster-demo-flutter-guest"
+QEMU_UNIT = "agl-qemu-runner@${QEMU_IMAGE}.service"
+
+do_install() {
+ # Install template unit links
+ install -d ${D}${systemd_system_unitdir}
+ ln -sf agl-qemu-runner@.service ${D}${systemd_system_unitdir}/${QEMU_UNIT}
+ install -d ${D}${systemd_system_unitdir}/multi-user.target.wants
+ ln -sf ${systemd_system_unitdir}/${QEMU_UNIT} ${D}${systemd_system_unitdir}/multi-user.target.wants/${QEMU_UNIT}
+
+ # Install conf file
+ install -d ${D}${sysconfdir}/agl-qemu-runner
+ install -m 0644 ${WORKDIR}/${QEMU_IMAGE}.conf ${D}${sysconfdir}/agl-qemu-runner/
+}
+
+FILES:${PN} += "${systemd_system_unitdir}"
+
+RDEPENDS:${PN} += "agl-qemu-runner qemu-config-vmnet0"
diff --git a/meta-agl-kvm-demo/recipes-config/qemu-config/qemu-config-agl-ivi-demo-flutter-guest-preconfigured.bb b/meta-agl-kvm-demo/recipes-config/qemu-config/qemu-config-agl-ivi-demo-flutter-guest-preconfigured.bb
new file mode 100644
index 00000000..642876b5
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-config/qemu-config/qemu-config-agl-ivi-demo-flutter-guest-preconfigured.bb
@@ -0,0 +1,3 @@
+require qemu-config-agl-ivi-demo-flutter-guest.bb
+
+QEMU_IMAGE = "agl-ivi-demo-flutter-guest-preconfigured"
diff --git a/meta-agl-kvm-demo/recipes-config/qemu-config/qemu-config-agl-ivi-demo-flutter-guest.bb b/meta-agl-kvm-demo/recipes-config/qemu-config/qemu-config-agl-ivi-demo-flutter-guest.bb
new file mode 100644
index 00000000..cfab8b1a
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-config/qemu-config/qemu-config-agl-ivi-demo-flutter-guest.bb
@@ -0,0 +1,29 @@
+SUMMARY = "Setting files for agl-ivi-demo-plaform-flutter guest VM"
+LICENSE = "MIT"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
+
+inherit systemd allarch
+
+SRC_URI = "file://${QEMU_IMAGE}.conf"
+
+do_configure[noexec] = "1"
+do_compile[noexec] = "1"
+
+QEMU_IMAGE = "agl-ivi-demo-flutter-guest"
+QEMU_UNIT = "agl-qemu-runner@${QEMU_IMAGE}.service"
+
+do_install() {
+ # Install template unit links
+ install -d ${D}${systemd_system_unitdir}
+ ln -sf agl-qemu-runner@.service ${D}${systemd_system_unitdir}/${QEMU_UNIT}
+ install -d ${D}${systemd_system_unitdir}/multi-user.target.wants
+ ln -sf ${systemd_system_unitdir}/${QEMU_UNIT} ${D}${systemd_system_unitdir}/multi-user.target.wants/${QEMU_UNIT}
+
+ # Install conf file
+ install -d ${D}${sysconfdir}/agl-qemu-runner
+ install -m 0644 ${WORKDIR}/${QEMU_IMAGE}.conf ${D}${sysconfdir}/agl-qemu-runner/
+}
+
+FILES:${PN} += "${systemd_system_unitdir}"
+
+RDEPENDS:${PN} += "agl-qemu-runner qemu-config-vmnet0"
diff --git a/meta-agl-kvm-demo/recipes-config/qemu-config/qemu-config-vmnet0.bb b/meta-agl-kvm-demo/recipes-config/qemu-config/qemu-config-vmnet0.bb
new file mode 100644
index 00000000..a5a12982
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-config/qemu-config/qemu-config-vmnet0.bb
@@ -0,0 +1,38 @@
+SUMMARY = "Setting files for QEMU networking for guest VMs"
+LICENSE = "MIT"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
+
+inherit systemd allarch
+
+SRC_URI = "file://vmnet0.netdev \
+ file://vmnet0.network \
+ file://bridge.conf \
+ file://dnsmasq-qemu.conf \
+ file://connman-nodnsproxy.conf \
+"
+
+do_configure[noexec] = "1"
+do_compile[noexec] = "1"
+
+do_install() {
+ # Install systemd-networkd vmnet0 configuration
+ install -d ${D}${systemd_unitdir}/network
+ install -m 0644 ${WORKDIR}/vmnet0.netdev ${D}${systemd_unitdir}/network/
+ install -m 0644 ${WORKDIR}/vmnet0.network ${D}${systemd_unitdir}/network/
+
+ # Install QEMU bridge configuration
+ install -d ${D}${sysconfdir}/qemu
+ install -m 0644 ${WORKDIR}/bridge.conf ${D}${sysconfdir}/qemu/
+
+ # Configure dnsmasq to serve DHCP to the guests
+ install -d ${D}${sysconfdir}/dnsmasq.d
+ install -m 0644 ${WORKDIR}/dnsmasq-qemu.conf ${D}${sysconfdir}/dnsmasq.d/
+
+ # Disable ConnMan's local DNS proxy to not conflict with dnsmasq
+ install -d ${D}${systemd_system_unitdir}/connman.service.d/
+ install -m 0644 ${WORKDIR}/connman-nodnsproxy.conf ${D}${systemd_system_unitdir}/connman.service.d/
+}
+
+FILES:${PN} += "${systemd_unitdir}/network ${systemd_system_unitdir}"
+
+RDEPENDS:${PN} += "agl-qemu-runner dnsmasq connman"
diff --git a/meta-agl-kvm-demo/recipes-extended/agl-qemu-runner/agl-qemu-runner.bb b/meta-agl-kvm-demo/recipes-extended/agl-qemu-runner/agl-qemu-runner.bb
new file mode 100644
index 00000000..b3e3a67d
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-extended/agl-qemu-runner/agl-qemu-runner.bb
@@ -0,0 +1,25 @@
+SUMMARY = "AGL simple QEMU runner script"
+LICENSE = "Apache-2.0"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
+
+inherit systemd allarch
+
+SRC_URI = "file://agl-qemu-runner.sh \
+ file://agl-qemu-runner@.service \
+"
+
+do_configure[noexec] = "1"
+do_compile[noexec] = "1"
+
+do_install() {
+ # Install template unit
+ install -d ${D}${systemd_system_unitdir}
+ install -m 0644 ${WORKDIR}/agl-qemu-runner@.service ${D}${systemd_system_unitdir}/
+
+ # Install script
+ install -D -m 0755 ${WORKDIR}/agl-qemu-runner.sh ${D}${sbindir}/agl-qemu-runner.sh
+}
+
+FILES:${PN} += "${systemd_system_unitdir}"
+
+RDEPENDS:${PN} += "bash qemu"
diff --git a/meta-agl-kvm-demo/recipes-extended/agl-qemu-runner/files/agl-qemu-runner.sh b/meta-agl-kvm-demo/recipes-extended/agl-qemu-runner/files/agl-qemu-runner.sh
new file mode 100755
index 00000000..f0136b6d
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-extended/agl-qemu-runner/files/agl-qemu-runner.sh
@@ -0,0 +1,72 @@
+#!/bin/bash
+# SPDX-License-Identifier: Apache-2.0
+
+if [ -z "$1" ]; then
+ echo "Usage: ${basename $0} <image name>"
+ exit 1
+fi
+image="$1"
+
+conf="/etc/agl-qemu-runner/${image}.conf"
+if [ ! -f "$conf" ]; then
+ echo "No configuration file $conf"
+ exit 1
+fi
+
+. $conf
+
+arch="$(uname -m)"
+if [ -z "$QEMU_IMAGE_ARCH" ]; then
+ QEMU_IMAGE_ARCH="virtio-${arch}"
+fi
+
+disk="/var/lib/machines/${image}/${image}-${QEMU_IMAGE_ARCH}.ext4"
+if [ ! -f "$disk" ]; then
+ echo "No disk image for $image"
+ exit 1
+fi
+kernel="/var/lib/machines/${image}/Image-${QEMU_IMAGE_ARCH}.bin"
+if [ ! -f "$kernel" ]; then
+ echo "No kernel for $image"
+ exit 1
+fi
+
+TASKSET_CMD=""
+if [ -n "$QEMU_TASKSET_CPUS" ]; then
+ TASKSET_CMD="taskset -c ${QEMU_TASKSET_CPUS}"
+fi
+
+export SDL_VIDEODRIVER=wayland
+export XDG_RUNTIME_DIR=/run/user/1001
+# The following may be needed if the socket is not wayland-0, as SDL
+# seems to lack detection logic for that case.
+#export WAYLAND_DISPLAY=wayland-1
+
+# This sets the XDG app id, which we need for setting outputs with
+# agl-compositor. If QEMU_XDG_APP_ID is not set, the image name
+# is used.
+export SDL_VIDEO_WAYLAND_WMCLASS="${QEMU_XDG_APP_ID:-${image}}"
+
+${TASKSET_CMD} \
+qemu-system-${arch} \
+ -enable-kvm \
+ -machine virt,gic-version=max,iommu=smmuv3 \
+ -cpu host \
+ ${QEMU_SMP_OPT} \
+ ${QEMU_MEM_OPT} \
+ -kernel $kernel \
+ -append "${QEMU_KERNEL_CMDLINE_APPEND}" \
+ -drive id=disk0,file=${disk},format=raw,if=none \
+ -serial mon:pty \
+ -object rng-random,filename=/dev/urandom,id=rng0 \
+ -device virtio-blk-device,drive=disk0 \
+ -device virtio-rng-device,rng=rng0 \
+ ${QEMU_NET_OPT} \
+ ${QEMU_INPUT_OPT} \
+ -global virtio-mmio.force-legacy=false \
+ -device virtio-gpu-gl-device \
+ -display sdl,gl=on -vga std \
+ ${QEMU_AUDIO_OPT} \
+ ${QEMU_CAN_OPT} \
+ ${QEMU_EXTRA_OPT} \
+ -full-screen
diff --git a/meta-agl-kvm-demo/recipes-extended/agl-qemu-runner/files/agl-qemu-runner@.service b/meta-agl-kvm-demo/recipes-extended/agl-qemu-runner/files/agl-qemu-runner@.service
new file mode 100644
index 00000000..d19c529f
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-extended/agl-qemu-runner/files/agl-qemu-runner@.service
@@ -0,0 +1,12 @@
+[Unit]
+Requires=native-shell-client.service
+After=native-shell-client.service
+
+[Service]
+Type=simple
+ExecStart=/usr/sbin/agl-qemu-runner.sh %i
+#Restart=on-failure
+Restart=no
+
+[Install]
+WantedBy=multi-user.target
diff --git a/meta-agl-kvm-demo/recipes-graphics/wayland/weston-ini-conf.bbappend b/meta-agl-kvm-demo/recipes-graphics/wayland/weston-ini-conf.bbappend
new file mode 100644
index 00000000..423a4694
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-graphics/wayland/weston-ini-conf.bbappend
@@ -0,0 +1 @@
+require ${@bb.utils.contains('AGL_FEATURES', 'agldemo', 'weston-ini-conf_agldemo.inc', '', d)}
diff --git a/meta-agl-kvm-demo/recipes-graphics/wayland/weston-ini-conf/weston.ini.kvm.in b/meta-agl-kvm-demo/recipes-graphics/wayland/weston-ini-conf/weston.ini.kvm.in
new file mode 100644
index 00000000..3369853f
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-graphics/wayland/weston-ini-conf/weston.ini.kvm.in
@@ -0,0 +1,18 @@
+[core]
+backend=drm-backend.so
+require-input=false
+modules=systemd-notify.so
+
+[shell]
+locking=true
+panel-position=none
+
+# A display is connected to HDMI-A-1
+[output]
+name=HDMI-A-1
+agl-shell-app-id=@GUEST_VM1_ID@
+
+# A display is connected to HDMI-A-2
+[output]
+name=HDMI-A-2
+agl-shell-app-id=@GUEST_VM2_ID@
diff --git a/meta-agl-kvm-demo/recipes-graphics/wayland/weston-ini-conf_agldemo.inc b/meta-agl-kvm-demo/recipes-graphics/wayland/weston-ini-conf_agldemo.inc
new file mode 100644
index 00000000..28f871a8
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-graphics/wayland/weston-ini-conf_agldemo.inc
@@ -0,0 +1,66 @@
+FILESEXTRAPATHS:prepend := "${THISDIR}/weston-ini-conf:"
+
+SRC_URI += "file://weston.ini.kvm.in"
+
+REMOTING_KVM_OUTPUT_HOST ?= "172.16.10.3"
+
+# Default app IDs for KVM guests
+GUEST_VM1_ID ?= "agl-ivi-demo"
+GUEST_VM2_ID ?= "agl-cluster-demo"
+
+do_configure:append() {
+ # KVM guest cluster version
+ sed -e "s#host=.*#host=${REMOTING_KVM_OUTPUT_HOST}#" \
+ -e "s#port=.*#port=${REMOTING_OUTPUT_PORT}#" \
+ ${WORKDIR}/remote-output.cfg.in > ${WORKDIR}/remote-output-kvm.cfg
+}
+
+do_compile:append() {
+ # Create KVM remoting enabled versions of the default portrait
+ # and landscape demo IVI configurations
+ for c in weston.ini.default weston.ini.landscape; do
+ cp ${WORKDIR}/$c ${WORKDIR}/${c}-remoting-kvm
+ echo >> ${WORKDIR}/${c}-remoting-kvm
+ cat ${WORKDIR}/remote-output-kvm.cfg >> ${WORKDIR}/${c}-remoting-kvm
+ done
+
+ # Create a canned configuration for the a KVM host, filling in
+ # the guest VM application ids to pin them to specific outputs.
+ rm -f ${WORKDIR}/weston.ini.kvm
+ sed -e "s/@GUEST_VM1_ID@/${GUEST_VM1_ID}/g" \
+ -e "s/@GUEST_VM2_ID@/${GUEST_VM2_ID}/g" \
+ ${WORKDIR}/weston.ini.kvm.in > ${WORKDIR}/weston.ini.kvm
+}
+
+do_install:append() {
+ install -m 0644 ${WORKDIR}/weston.ini.default-remoting-kvm ${D}${weston_ini_dir}/
+ install -m 0644 ${WORKDIR}/weston.ini.landscape-remoting-kvm ${D}${weston_ini_dir}/
+ install -m 0644 ${WORKDIR}/weston.ini.kvm ${D}${weston_ini_dir}/
+}
+
+# remoting-kvm
+
+PACKAGE_BEFORE_PN += "${PN}-remoting-kvm"
+FILES:${PN}-remoting-kvm = "${weston_ini_dir}/weston.ini.default-remoting-kvm"
+RPROVIDES:${PN}-remoting-kvm = "weston-ini"
+ALTERNATIVE:${PN}-remoting-kvm = "weston.ini"
+ALTERNATIVE_TARGET_${PN}-remoting-kvm = "${weston_ini_dir}/weston.ini.default-remoting-kvm"
+ALTERNATIVE_PRIORITY_${PN}-remoting-kvm = "35"
+
+# landscape-remoting-kvm
+
+PACKAGE_BEFORE_PN += "${PN}-landscape-remoting-kvm"
+FILES:${PN}-landscape-remoting-kvm = "${weston_ini_dir}/weston.ini.landscape-remoting-kvm"
+RPROVIDES:${PN}-landscape-remoting-kvm = "weston-ini"
+ALTERNATIVE:${PN}-landscape-remoting-kvm = "weston.ini"
+ALTERNATIVE_TARGET_${PN}-landscape-remoting-kvm = "${weston_ini_dir}/weston.ini.landscape-remoting-kvm"
+ALTERNATIVE_PRIORITY_${PN}-landscape-remoting-kvm = "36"
+
+# kvm
+
+PACKAGE_BEFORE_PN += "${PN}-kvm"
+FILES:${PN}-kvm = "${weston_ini_dir}/weston.ini.kvm"
+RPROVIDES:${PN}-kvm = "weston-ini"
+ALTERNATIVE:${PN}-kvm = "weston.ini"
+ALTERNATIVE_TARGET_${PN}-kvm = "${weston_ini_dir}/weston.ini.kvm"
+ALTERNATIVE_PRIORITY_${PN}-kvm = "40"
diff --git a/meta-agl-kvm-demo/recipes-platform/images/agl-cluster-demo-flutter-guest-preconfigured.bb b/meta-agl-kvm-demo/recipes-platform/images/agl-cluster-demo-flutter-guest-preconfigured.bb
new file mode 100644
index 00000000..d5c8fcec
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-platform/images/agl-cluster-demo-flutter-guest-preconfigured.bb
@@ -0,0 +1,12 @@
+require agl-cluster-demo-flutter-guest.bb
+
+SUMMARY = "AGL KVM demo guest preconfigured cluster Flutter image"
+
+# The cluster screen is rotated in the full demo setup, so the
+# default compositor configuration needs to be replaced.
+IMAGE_INSTALL:remove = "weston-ini-conf-landscape"
+
+IMAGE_INSTALL += " \
+ psplash-inverted-config \
+ weston-ini-conf-landscape-inverted \
+"
diff --git a/meta-agl-kvm-demo/recipes-platform/images/agl-cluster-demo-flutter-guest.bb b/meta-agl-kvm-demo/recipes-platform/images/agl-cluster-demo-flutter-guest.bb
new file mode 100644
index 00000000..3cde0d07
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-platform/images/agl-cluster-demo-flutter-guest.bb
@@ -0,0 +1,8 @@
+require recipes-platform/images/agl-cluster-demo-flutter.bb
+
+SUMMARY = "AGL KVM demo guest cluster Flutter image"
+
+# We do not want a local databroker instance
+IMAGE_FEATURES:remove = "kuksa-val-databroker"
+
+FLUTTER_CLUSTER_DASHBOARD_CONF = "flutter-cluster-dashboard-conf-kvm-demo"
diff --git a/meta-agl-kvm-demo/recipes-platform/images/agl-ivi-demo-flutter-guest-preconfigured.bb b/meta-agl-kvm-demo/recipes-platform/images/agl-ivi-demo-flutter-guest-preconfigured.bb
new file mode 100644
index 00000000..a98cb588
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-platform/images/agl-ivi-demo-flutter-guest-preconfigured.bb
@@ -0,0 +1,12 @@
+require agl-ivi-demo-flutter-guest.bb
+
+SUMMARY = "AGL KVM demo preconfigured guest IVI Flutter image"
+
+# KUKSA.val always runs externally
+IMAGE_FEATURES:remove = "kuksa-val-databroker"
+
+# Everything runs on the host for now
+PLATFORM_SERVICES_INSTALL = ""
+
+# We do not want weston-terminal visible
+IMAGE_INSTALL:remove = "weston-terminal-conf"
diff --git a/meta-agl-kvm-demo/recipes-platform/images/agl-ivi-demo-flutter-guest.bb b/meta-agl-kvm-demo/recipes-platform/images/agl-ivi-demo-flutter-guest.bb
new file mode 100644
index 00000000..f6e748bf
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-platform/images/agl-ivi-demo-flutter-guest.bb
@@ -0,0 +1,14 @@
+require recipes-platform/images/agl-ivi-demo-flutter.bb
+
+SUMMARY = "AGL KVM demo guest IVI Flutter image"
+
+# We assume there's always a cluster in the KVM demo
+IMAGE_FEATURES += "agl-demo-cluster-support"
+
+FLUTTER_ICS_HOMESCREEN_CONF = "flutter-ics-homescreen-conf-kvm-demo"
+ONDEMANDNAVI_CONF = "ondemandnavi-conf-kvm-demo"
+TBTNAVI_CONF = "tbtnavi-conf-kvm-demo"
+
+IMAGE_INSTALL += " \
+ weston-ini-conf-remoting-kvm \
+"
diff --git a/meta-agl-kvm-demo/recipes-platform/images/agl-kvm-demo-flutter-preconfigured.bb b/meta-agl-kvm-demo/recipes-platform/images/agl-kvm-demo-flutter-preconfigured.bb
new file mode 100644
index 00000000..9b02c68c
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-platform/images/agl-kvm-demo-flutter-preconfigured.bb
@@ -0,0 +1,38 @@
+LICENSE = "MIT"
+
+require agl-kvm-demo.bb
+
+SUMMARY = "AGL KVM+QEMU preconfigured Flutter demo image"
+
+# If building with "agl-kvm-host-kuksa", the databroker and likely
+# some clients run on the host
+IMAGE_FEATURES += " \
+ kuksa-val-databroker \
+ kuksa-val-databroker-client \
+"
+
+# Until virtio sound is workable with QEMU, run the audio using
+# services on the host for a better demo experience. At the
+# moment, this also includes the HVAC service since it does not
+# make sense to try to make things more fine-grained with respect
+# to configuration for where things expect to find the databroker.
+# It will need to be revisited when virtio-snd, virtio-gpio, etc.
+# become feasible to use.
+HOST_AUDIO_INSTALL = " \
+ packagegroup-agl-ivi-services-platform \
+ packagegroup-agl-ivi-multimedia-platform \
+ agl-service-radio-conf-kvm-demo \
+ packagegroup-pipewire \
+ wireplumber-config-agl \
+ wireplumber-policy-config-agl \
+ udisks2 \
+ ${@bb.utils.contains("DISTRO_FEATURES", "agl-devel", "packagegroup-pipewire-tools mpc" , "", d)} \
+"
+
+IMAGE_INSTALL += "\
+ kuksa-databroker-agl-demo-cluster \
+ ${HOST_AUDIO_INSTALL} \
+"
+
+GUEST_VM1_IMAGE = "agl-ivi-demo-flutter-guest-preconfigured"
+GUEST_VM2_IMAGE = "agl-cluster-demo-flutter-guest-preconfigured"
diff --git a/meta-agl-kvm-demo/recipes-platform/images/agl-kvm-demo.bb b/meta-agl-kvm-demo/recipes-platform/images/agl-kvm-demo.bb
new file mode 100644
index 00000000..33f2b921
--- /dev/null
+++ b/meta-agl-kvm-demo/recipes-platform/images/agl-kvm-demo.bb
@@ -0,0 +1,69 @@
+SUMMARY = "AGL KVM+QEMU demo image"
+LICENSE = "MIT"
+
+require recipes-platform/images/agl-image-compositor.bb
+require recipes-platform/images/agl-demo-features.inc
+
+IMAGE_FEATURES += "splash package-management ssh-server-openssh"
+
+IMAGE_FEATURES += " \
+ ${@bb.utils.contains("DISTRO_FEATURES", "agl-devel", "can-test-tools" , "", d)} \
+"
+
+# Add packages for KVM+QEMU demo platform here
+IMAGE_INSTALL += " \
+ packagegroup-agl-core-connectivity \
+ kernel-image \
+ agl-compositor \
+ weston-ini-conf-kvm \
+ output-udev-conf \
+ native-shell-client \
+ qemu \
+ ${QEMU_GUEST_CONFIGS} \
+ util-linux-taskset \
+ screen \
+ simple-can-simulator \
+ alsa-utils \
+"
+
+# Potential size reduction options
+#IMAGE_LINGUAS = " "
+#NO_RECOMMENDATIONS = "1"
+
+GUEST_MACHINE ?= "virtio-${TUNE_ARCH}"
+
+GUEST_VM1_IMAGE ?= "agl-ivi-demo-flutter-guest"
+GUEST_VM2_IMAGE ?= "agl-cluster-demo-flutter-guest"
+
+GUEST_IMAGES ?= "agl-kvm-guest:${GUEST_VM1_IMAGE} agl-kvm-guest:${GUEST_VM2_IMAGE}"
+
+QEMU_GUEST_CONFIGS ?= ""
+
+python __anonymous() {
+ for c in (d.getVar('GUEST_IMAGES') or "").split():
+ (mc, image) = c.split(':')
+ dependency = 'mc::' + mc + ':' + image + ':do_image_complete'
+ d.appendVarFlag('do_rootfs', 'mcdepends', ' ' + dependency)
+
+ # Assume there is a qemu-config-X package for guest image X
+ d.appendVar('QEMU_GUEST_CONFIGS', ' ' + 'qemu-config-' + image)
+}
+
+install_guest_images() {
+ for c in ${GUEST_IMAGES}; do
+ config=${c%:*}
+ image=${c#*:}
+ name=${image}
+ rm -rf ${IMAGE_ROOTFS}/var/lib/machines/${name}
+ install -m 0755 -d ${IMAGE_ROOTFS}/var/lib/machines/${name}
+ src="${TOPDIR}/tmp-${config}/deploy/images/${GUEST_MACHINE}/${image}-${GUEST_MACHINE}.ext4"
+ bbnote "Installing ${src}"
+ install -m 0600 ${src} ${IMAGE_ROOTFS}/var/lib/machines/${name}/
+ # Placeholder until booting from kernel in VM image is worked out
+ install -m 0600 ${TOPDIR}/tmp-${config}/deploy/images/${GUEST_MACHINE}/Image-${GUEST_MACHINE}.bin ${IMAGE_ROOTFS}/var/lib/machines/${name}/
+ done
+}
+
+ROOTFS_POSTPROCESS_COMMAND += "install_guest_images; "
+
+IMAGE_ROOTFS_EXTRA_SPACE:append = "${@bb.utils.contains("DISTRO_FEATURES", "systemd", " + 4096", "" ,d)}"