summaryrefslogtreecommitdiffstats
path: root/recipes-core
diff options
context:
space:
mode:
authorBruce Ashfield <bruce.ashfield@gmail.com>2026-02-15 04:35:55 +0000
committerBruce Ashfield <bruce.ashfield@gmail.com>2026-02-26 01:05:01 +0000
commit57d267db7878180d1ecd1936df5284550d0031c3 (patch)
treeb2205ccb2e6114fdda4384518d4b9047209756ef /recipes-core
parent0fe8c4444f3199b862a4ba52b2b62b5f9b2af85f (diff)
downloadmeta-virtualization-57d267db7878180d1ecd1936df5284550d0031c3.tar.gz
vxn: add Xen DomU container runtime with OCI image support
vxn runs OCI containers as Xen DomU guests — the VM IS the container. No Docker/containerd runs inside the guest; the init script directly mounts the container rootfs and execs the entrypoint via chroot. Host-side (Dom0): - vxn.sh: Docker-like CLI wrapper (sets HYPERVISOR=xen) - vrunner-backend-xen.sh: Xen xl backend for vrunner - hv_prepare_container(): pulls OCI images via skopeo, resolves entrypoint from OCI config using jq on host - xl create for VM lifecycle (PVH on aarch64, PV on x86_64) - Bridge networking with iptables DNAT for port forwards - Console capture via xl console for ephemeral mode Guest-side (DomU): - vxn-init.sh: mounts container rootfs from input disk, extracts OCI layers, execs entrypoint via chroot - Supports containers with or without /bin/sh - grep/sed fallback for OCI config parsing (no jq needed) - Daemon mode with command loop on hvc1 - vcontainer-init-common.sh: hypervisor detection, head -n fix - vcontainer-preinit.sh: init selection via vcontainer.init= Build system: - vxn-initramfs-create.inc: assembles boot blobs from vruntime multiconfig, injects vxn-init.sh into rootfs squashfs - vxn_1.0.bb: Dom0 package with scripts + blobs - nostamp on install/package chain (blobs from DEPLOY_DIR are untracked by sstate) - vxn.cfg: Xen PV kernel config fragment Tested: vxn -it --no-daemon run --rm hello-world Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
Diffstat (limited to 'recipes-core')
-rw-r--r--recipes-core/vxn/vxn-initramfs-create.inc223
-rw-r--r--recipes-core/vxn/vxn-initramfs-create_1.0.bb43
-rw-r--r--recipes-core/vxn/vxn_1.0.bb167
3 files changed, 433 insertions, 0 deletions
diff --git a/recipes-core/vxn/vxn-initramfs-create.inc b/recipes-core/vxn/vxn-initramfs-create.inc
new file mode 100644
index 00000000..bde8bba9
--- /dev/null
+++ b/recipes-core/vxn/vxn-initramfs-create.inc
@@ -0,0 +1,223 @@
1# SPDX-FileCopyrightText: Copyright (C) 2025 Bruce Ashfield
2#
3# SPDX-License-Identifier: MIT
4#
5# vxn-initramfs-create.inc
6# ===========================================================================
7# Shared code for building Xen DomU boot blobs for vxn
8# ===========================================================================
9#
10# This .inc file packages boot blobs for vxn (vcontainer on Xen).
11# It reuses the same initramfs and rootfs images built by vruntime
12# multiconfig (same images as vdkr/vpdmn), since the init scripts
13# detect the hypervisor at boot time.
14#
15# The kernel from vruntime already includes Xen PV support via vxn.cfg
16# fragment (added to DISTRO_FEATURES in vruntime.conf).
17#
18# Required variables from including recipe:
19# VXN_RUNTIME - runtime to source blobs from ("vdkr" or "vpdmn")
20#
21# Boot flow on Xen Dom0:
22# xl create <domain.cfg>
23# -> Xen boots kernel + tiny initramfs in DomU
24# -> preinit mounts rootfs.img from /dev/xvda
25# -> switch_root into rootfs.img
26# -> init detects Xen, uses /dev/xvd* and trans=xen
27#
28# ===========================================================================
29
30HOMEPAGE = "https://git.yoctoproject.org/meta-virtualization/"
31LICENSE = "MIT"
32LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
33
34inherit deploy
35
36EXCLUDE_FROM_WORLD = "1"
37DEPENDS = "squashfs-tools-native"
38
39# Default to vdkr (Docker) as the source runtime
40VXN_RUNTIME ?= "vdkr"
41
42# Always rebuild - init script injection must not be sstate-cached
43# because the rootfs.img content comes from the deploy dir (untracked)
44SSTATE_SKIP_CREATION = "1"
45do_compile[nostamp] = "1"
46do_deploy[nostamp] = "1"
47
48python () {
49
50 mc = d.getVar('VXN_MULTICONFIG')
51 runtime = d.getVar('VXN_RUNTIME')
52 bbmulticonfig = (d.getVar('BBMULTICONFIG') or "").split()
53 if mc in bbmulticonfig:
54 # All blobs come from the vruntime multiconfig - kernel, initramfs, rootfs
55 mcdeps = ' '.join([
56 'mc::%s:%s-tiny-initramfs-image:do_image_complete' % (mc, runtime),
57 'mc::%s:%s-rootfs-image:do_image_complete' % (mc, runtime),
58 'mc::%s:virtual/kernel:do_deploy' % mc,
59 ])
60 d.setVarFlag('do_compile', 'mcdepends', mcdeps)
61}
62
63INHIBIT_DEFAULT_DEPS = "1"
64
65# Init scripts to inject into the rootfs squashfs
66FILESEXTRAPATHS:prepend := "${THISDIR}/../../recipes-containers/vcontainer/files:"
67SRC_URI = "\
68 file://vxn-init.sh \
69 file://vcontainer-init-common.sh \
70"
71
72S = "${UNPACKDIR}"
73B = "${WORKDIR}/build"
74
75def vxn_get_kernel_image_name(d):
76 arch = d.getVar('TARGET_ARCH')
77 if arch == 'aarch64':
78 return 'Image'
79 elif arch in ['x86_64', 'i686', 'i586']:
80 return 'bzImage'
81 elif arch == 'arm':
82 return 'zImage'
83 return 'Image'
84
85def vxn_get_multiconfig_name(d):
86 arch = d.getVar('TARGET_ARCH')
87 if arch == 'aarch64':
88 return 'vruntime-aarch64'
89 elif arch in ['x86_64', 'i686', 'i586']:
90 return 'vruntime-x86-64'
91 return 'vruntime-aarch64'
92
93def vxn_get_blob_arch(d):
94 arch = d.getVar('TARGET_ARCH')
95 if arch == 'aarch64':
96 return 'aarch64'
97 elif arch in ['x86_64', 'i686', 'i586']:
98 return 'x86_64'
99 return 'aarch64'
100
101KERNEL_IMAGETYPE_INITRAMFS = "${@vxn_get_kernel_image_name(d)}"
102VXN_MULTICONFIG = "${@vxn_get_multiconfig_name(d)}"
103BLOB_ARCH = "${@vxn_get_blob_arch(d)}"
104
105VXN_MC_DEPLOY = "${TOPDIR}/tmp-${VXN_MULTICONFIG}/deploy/images/${MACHINE}"
106
107do_compile() {
108 mkdir -p ${B}
109
110 MC_TMPDIR="${TOPDIR}/tmp-${VXN_MULTICONFIG}"
111 MC_DEPLOY="${MC_TMPDIR}/deploy/images/${MACHINE}"
112
113 # =========================================================================
114 # PART 1: COPY TINY INITRAMFS (same as vdkr/vpdmn)
115 # =========================================================================
116 bbnote "Copying tiny initramfs from image build..."
117
118 INITRAMFS_SRC="${MC_DEPLOY}/${VXN_RUNTIME}-tiny-initramfs-image-${MACHINE}.cpio.gz"
119
120 if [ ! -f "${INITRAMFS_SRC}" ]; then
121 bbfatal "Initramfs not found at ${INITRAMFS_SRC}. Build it first with: bitbake mc:${VXN_MULTICONFIG}:${VXN_RUNTIME}-tiny-initramfs-image"
122 fi
123
124 cp "${INITRAMFS_SRC}" ${B}/initramfs.cpio.gz
125 INITRAMFS_SIZE=$(stat -c%s ${B}/initramfs.cpio.gz)
126 bbnote "Initramfs copied: ${INITRAMFS_SIZE} bytes ($(expr ${INITRAMFS_SIZE} / 1024)KB)"
127
128 # =========================================================================
129 # PART 2: COPY ROOTFS (same squashfs, works under both QEMU and Xen)
130 # =========================================================================
131 bbnote "Copying rootfs from image build..."
132
133 ROOTFS_SRC="${MC_DEPLOY}/${VXN_RUNTIME}-rootfs-image-${MACHINE}.rootfs.squashfs"
134
135 if [ ! -f "${ROOTFS_SRC}" ]; then
136 bbfatal "Rootfs image not found at ${ROOTFS_SRC}. Build it first with: bitbake mc:${VXN_MULTICONFIG}:${VXN_RUNTIME}-rootfs-image"
137 fi
138
139 cp "${ROOTFS_SRC}" ${B}/rootfs.img
140 ROOTFS_SIZE=$(stat -c%s ${B}/rootfs.img)
141 bbnote "Rootfs image copied: ${ROOTFS_SIZE} bytes ($(expr ${ROOTFS_SIZE} / 1024 / 1024)MB)"
142
143 # Inject vxn init scripts into the rootfs squashfs
144 bbnote "Injecting vxn init scripts into rootfs..."
145 UNSQUASH_DIR="${B}/rootfs-unsquash"
146 rm -rf "${UNSQUASH_DIR}"
147 unsquashfs -d "${UNSQUASH_DIR}" ${B}/rootfs.img
148 install -m 0755 ${S}/vxn-init.sh ${UNSQUASH_DIR}/vxn-init.sh
149 install -m 0755 ${S}/vcontainer-init-common.sh ${UNSQUASH_DIR}/vcontainer-init-common.sh
150 rm -f ${B}/rootfs.img
151 mksquashfs "${UNSQUASH_DIR}" ${B}/rootfs.img -noappend -comp xz
152 rm -rf "${UNSQUASH_DIR}"
153 ROOTFS_SIZE=$(stat -c%s ${B}/rootfs.img)
154 bbnote "Rootfs with vxn init scripts: ${ROOTFS_SIZE} bytes ($(expr ${ROOTFS_SIZE} / 1024 / 1024)MB)"
155
156 # =========================================================================
157 # PART 3: COPY KERNEL (Xen PV-capable via vxn.cfg fragment)
158 # =========================================================================
159 bbnote "Copying kernel image..."
160 KERNEL_FILE="${DEPLOY_DIR_IMAGE}/${KERNEL_IMAGETYPE_INITRAMFS}"
161 if [ -f "${KERNEL_FILE}" ]; then
162 cp "${KERNEL_FILE}" ${B}/kernel
163 KERNEL_SIZE=$(stat -c%s ${B}/kernel)
164 bbnote "Kernel copied: ${KERNEL_SIZE} bytes ($(expr ${KERNEL_SIZE} / 1024 / 1024)MB)"
165 else
166 bbwarn "Kernel not found at ${KERNEL_FILE}"
167 fi
168}
169
170# This is a deploy-only recipe - no packages produced.
171# PACKAGES="" prevents the rootfs task from looking for package manifests.
172PACKAGES = ""
173do_install[noexec] = "1"
174do_package[noexec] = "1"
175do_packagedata[noexec] = "1"
176do_package_write_rpm[noexec] = "1"
177do_package_write_ipk[noexec] = "1"
178do_package_write_deb[noexec] = "1"
179do_populate_sysroot[noexec] = "1"
180
181do_deploy() {
182 install -d ${DEPLOYDIR}/vxn/${BLOB_ARCH}
183
184 if [ -f ${B}/initramfs.cpio.gz ]; then
185 install -m 0644 ${B}/initramfs.cpio.gz ${DEPLOYDIR}/vxn/${BLOB_ARCH}/
186 bbnote "Deployed initramfs.cpio.gz to vxn/${BLOB_ARCH}/"
187 fi
188
189 if [ -f ${B}/rootfs.img ]; then
190 install -m 0644 ${B}/rootfs.img ${DEPLOYDIR}/vxn/${BLOB_ARCH}/
191 bbnote "Deployed rootfs.img to vxn/${BLOB_ARCH}/"
192 fi
193
194 if [ -f ${B}/kernel ]; then
195 install -m 0644 ${B}/kernel ${DEPLOYDIR}/vxn/${BLOB_ARCH}/${KERNEL_IMAGETYPE_INITRAMFS}
196 bbnote "Deployed kernel as vxn/${BLOB_ARCH}/${KERNEL_IMAGETYPE_INITRAMFS}"
197 fi
198
199 cat > ${DEPLOYDIR}/vxn/${BLOB_ARCH}/README << EOF
200vxn Boot Blobs (Xen DomU)
201==========================
202
203Built for: ${TARGET_ARCH}
204Machine: ${MACHINE}
205Multiconfig: ${VXN_MULTICONFIG}
206Source runtime: ${VXN_RUNTIME}
207Date: $(date)
208
209Files:
210 ${KERNEL_IMAGETYPE_INITRAMFS} - Kernel image (Xen PV-capable)
211 initramfs.cpio.gz - Tiny initramfs (busybox + preinit)
212 rootfs.img - Root filesystem with container tools
213
214Boot flow:
215 xl create <domain.cfg>
216 -> Xen boots kernel + initramfs in DomU
217 -> preinit detects Xen, mounts rootfs.img from /dev/xvda
218 -> switch_root into rootfs.img
219 -> init script runs container commands
220EOF
221}
222
223addtask deploy after do_compile before do_build
diff --git a/recipes-core/vxn/vxn-initramfs-create_1.0.bb b/recipes-core/vxn/vxn-initramfs-create_1.0.bb
new file mode 100644
index 00000000..edbef12f
--- /dev/null
+++ b/recipes-core/vxn/vxn-initramfs-create_1.0.bb
@@ -0,0 +1,43 @@
1# SPDX-FileCopyrightText: Copyright (C) 2025 Bruce Ashfield
2#
3# SPDX-License-Identifier: MIT
4#
5# vxn-initramfs-create_1.0.bb
6# ===========================================================================
7# Builds Xen DomU boot blobs for vxn
8# ===========================================================================
9#
10# This recipe packages boot blobs for vxn (vcontainer on Xen):
11# - A tiny initramfs (reused from vdkr/vpdmn build)
12# - The rootfs.img squashfs (same as vdkr, with HV detection in init)
13# - The kernel (Xen PV-capable via vxn.cfg fragment in vruntime)
14#
15# Boot flow on Xen Dom0:
16# xl create domain.cfg
17# -> Xen boots kernel + tiny initramfs in DomU
18# -> preinit detects Xen block prefix, mounts rootfs.img from /dev/xvda
19# -> switch_root into rootfs.img
20# -> vdkr-init.sh detects Xen via /proc/xen, uses xvd* devices
21#
22# ===========================================================================
23# BUILD INSTRUCTIONS
24# ===========================================================================
25#
26# For aarch64:
27# MACHINE=qemuarm64 bitbake vxn-initramfs-create
28#
29# For x86_64:
30# MACHINE=qemux86-64 bitbake vxn-initramfs-create
31#
32# Blobs are deployed to: tmp/deploy/images/${MACHINE}/vxn/
33#
34# ===========================================================================
35
36SUMMARY = "Build Xen DomU boot blobs for vxn"
37DESCRIPTION = "Packages kernel, initramfs and rootfs for running \
38 vcontainer workloads as Xen DomU guests."
39
40# Source blobs from vdkr (Docker) build - same rootfs works under Xen
41VXN_RUNTIME = "vdkr"
42
43require vxn-initramfs-create.inc
diff --git a/recipes-core/vxn/vxn_1.0.bb b/recipes-core/vxn/vxn_1.0.bb
new file mode 100644
index 00000000..2a36274a
--- /dev/null
+++ b/recipes-core/vxn/vxn_1.0.bb
@@ -0,0 +1,167 @@
1# SPDX-FileCopyrightText: Copyright (C) 2025 Bruce Ashfield
2#
3# SPDX-License-Identifier: MIT
4#
5# vxn_1.0.bb
6# ===========================================================================
7# Target integration package for vxn (vcontainer on Xen)
8# ===========================================================================
9#
10# This recipe installs vxn onto a Xen Dom0 target. It provides:
11# - vxn CLI wrapper (docker-like interface for Xen DomU containers)
12# - vrunner.sh (hypervisor-agnostic VM runner)
13# - vrunner-backend-xen.sh (Xen xl backend)
14# - vcontainer-common.sh (shared CLI code)
15# - Kernel, initramfs, and rootfs blobs for booting DomU guests
16#
17# The blobs are sourced from the vxn-initramfs-create recipe which
18# reuses the same rootfs images built by vdkr/vpdmn (the init scripts
19# detect the hypervisor at boot time).
20#
21# ===========================================================================
22# BUILD INSTRUCTIONS
23# ===========================================================================
24#
25# For aarch64 Dom0:
26# MACHINE=qemuarm64 bitbake vxn
27#
28# For x86_64 Dom0:
29# MACHINE=qemux86-64 bitbake vxn
30#
31# Add to a Dom0 image:
32# IMAGE_INSTALL:append = " vxn"
33#
34# Usage on Dom0:
35# vxn run hello-world # Run OCI container as Xen DomU
36# vxn vmemres start # Start persistent DomU (daemon mode)
37# vxn vexpose # Expose Docker API on Dom0
38#
39# ===========================================================================
40
41SUMMARY = "Docker CLI for Xen-based container execution"
42DESCRIPTION = "vxn provides a familiar docker-like CLI that executes commands \
43 inside a Xen DomU guest with Docker. It uses the vcontainer \
44 infrastructure with a Xen hypervisor backend."
45HOMEPAGE = "https://git.yoctoproject.org/meta-virtualization/"
46LICENSE = "MIT"
47LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
48
49inherit features_check
50REQUIRED_DISTRO_FEATURES = "xen"
51
52SRC_URI = "\
53 file://vxn.sh \
54 file://vrunner.sh \
55 file://vrunner-backend-xen.sh \
56 file://vrunner-backend-qemu.sh \
57 file://vcontainer-common.sh \
58"
59
60FILESEXTRAPATHS:prepend := "${THISDIR}/../../recipes-containers/vcontainer/files:"
61
62S = "${UNPACKDIR}"
63
64# Runtime dependencies on Dom0
65RDEPENDS:${PN} = "\
66 xen-tools-xl \
67 bash \
68 jq \
69 socat \
70 coreutils \
71 util-linux \
72 e2fsprogs \
73 skopeo \
74"
75
76# Blobs are sourced from vxn-initramfs-create deploy output.
77# Build blobs first: bitbake vxn-initramfs-create
78# No task dependency here - vxn-initramfs-create is deploy-only (no packages).
79# Adding any dependency from a packaged recipe to a deploy-only recipe
80# breaks do_rootfs (sstate manifest not found for package_write_rpm).
81
82# Blobs come from DEPLOY_DIR which is untracked by sstate hash.
83# nostamp on do_install alone is insufficient — do_package and
84# do_package_write_rpm have unchanged sstate hashes so they restore
85# the OLD RPM from cache, discarding the fresh do_install output.
86# Force the entire install→package→RPM chain to always re-run.
87do_install[nostamp] = "1"
88do_package[nostamp] = "1"
89do_packagedata[nostamp] = "1"
90do_package_write_rpm[nostamp] = "1"
91do_package_write_ipk[nostamp] = "1"
92do_package_write_deb[nostamp] = "1"
93
94def vxn_get_blob_arch(d):
95 arch = d.getVar('TARGET_ARCH')
96 if arch == 'aarch64':
97 return 'aarch64'
98 elif arch in ['x86_64', 'i686', 'i586']:
99 return 'x86_64'
100 return 'aarch64'
101
102def vxn_get_kernel_image_name(d):
103 arch = d.getVar('TARGET_ARCH')
104 if arch == 'aarch64':
105 return 'Image'
106 elif arch in ['x86_64', 'i686', 'i586']:
107 return 'bzImage'
108 elif arch == 'arm':
109 return 'zImage'
110 return 'Image'
111
112BLOB_ARCH = "${@vxn_get_blob_arch(d)}"
113KERNEL_IMAGETYPE_VXN = "${@vxn_get_kernel_image_name(d)}"
114
115VXN_DEPLOY = "${DEPLOY_DIR_IMAGE}"
116
117do_install() {
118 # Install CLI wrapper
119 install -d ${D}${bindir}
120 install -m 0755 ${S}/vxn.sh ${D}${bindir}/vxn
121
122 # Install shared scripts into libdir
123 install -d ${D}${libdir}/vxn
124 install -m 0755 ${S}/vrunner.sh ${D}${libdir}/vxn/
125 install -m 0755 ${S}/vrunner-backend-xen.sh ${D}${libdir}/vxn/
126 install -m 0755 ${S}/vrunner-backend-qemu.sh ${D}${libdir}/vxn/
127 install -m 0644 ${S}/vcontainer-common.sh ${D}${libdir}/vxn/
128
129 # Install blobs from vxn-initramfs-create deployment
130 # Layout must match what vrunner backends expect: $BLOB_DIR/<arch>/{Image,initramfs.cpio.gz,rootfs.img}
131 install -d ${D}${datadir}/vxn/${BLOB_ARCH}
132
133 VXN_BLOB_SRC="${VXN_DEPLOY}/vxn/${BLOB_ARCH}"
134 if [ -d "${VXN_BLOB_SRC}" ]; then
135 if [ -f "${VXN_BLOB_SRC}/${KERNEL_IMAGETYPE_VXN}" ]; then
136 install -m 0644 "${VXN_BLOB_SRC}/${KERNEL_IMAGETYPE_VXN}" ${D}${datadir}/vxn/${BLOB_ARCH}/
137 bbnote "Installed kernel ${KERNEL_IMAGETYPE_VXN}"
138 else
139 bbwarn "Kernel not found at ${VXN_BLOB_SRC}/${KERNEL_IMAGETYPE_VXN}"
140 fi
141
142 if [ -f "${VXN_BLOB_SRC}/initramfs.cpio.gz" ]; then
143 install -m 0644 "${VXN_BLOB_SRC}/initramfs.cpio.gz" ${D}${datadir}/vxn/${BLOB_ARCH}/
144 bbnote "Installed initramfs"
145 else
146 bbwarn "Initramfs not found at ${VXN_BLOB_SRC}/initramfs.cpio.gz"
147 fi
148
149 if [ -f "${VXN_BLOB_SRC}/rootfs.img" ]; then
150 install -m 0644 "${VXN_BLOB_SRC}/rootfs.img" ${D}${datadir}/vxn/${BLOB_ARCH}/
151 bbnote "Installed rootfs.img"
152 else
153 bbwarn "Rootfs not found at ${VXN_BLOB_SRC}/rootfs.img"
154 fi
155 else
156 bbwarn "VXN blob directory not found at ${VXN_BLOB_SRC}. Build with: bitbake vxn-initramfs-create"
157 fi
158}
159
160FILES:${PN} = "\
161 ${bindir}/vxn \
162 ${libdir}/vxn/ \
163 ${datadir}/vxn/ \
164"
165
166# Blobs are large binary files
167INSANE_SKIP:${PN} += "already-stripped"