[PATCH 4/5] guestfs: add ARM64 support with cross-architecture image customization

Daniel Gomez <[email protected]>
Newsgroups dev.linux.lists.kdevops
Message-ID <[email protected]>
From: Daniel Gomez <[email protected]>

Add ARM64 libguestfs support with architecture detection. Supports
both same-architecture (full customization) and cross-architecture
(firstboot-only) scenarios for flexible dev environments.

* Add ARM64 configuration and target detection
* Implement conditional virt-customize logic based on host/guest
architecture
* Same arch: Full kdevops user setup via --commands-from-file
* Cross arch: Limited firstboot commands due to binary compatibility
* Update libvirt templates and node generation for ARM64

Enables x86_64<->ARM64 development workflows while maintaining full
functionality for native ARM64->ARM64 and x86_64->x86_64 scenarios.

Generated-by: Claude AI
Signed-off-by: Daniel Gomez <[email protected]>
---
 kconfigs/Kconfig.guestfs                           | 18 ++++++---
 kconfigs/Kconfig.libvirt                           | 14 ++++++-
 kconfigs/arch/Kconfig                              |  3 ++
 playbooks/roles/base_image/tasks/custom-image.yml  | 44 +++++++++++++++++++++-
 playbooks/roles/gen_nodes/tasks/main.yml           |  2 +-
 .../roles/gen_nodes/templates/guestfs_virt.j2.xml  |  4 +-
 scripts/gen-nodes.Makefile                         |  4 ++
 7 files changed, 76 insertions(+), 13 deletions(-)

diff --git a/kconfigs/Kconfig.guestfs b/kconfigs/Kconfig.guestfs
index d1e922a6..52506a3b 100644
--- a/kconfigs/Kconfig.guestfs
+++ b/kconfigs/Kconfig.guestfs
@@ -30,9 +30,12 @@ config GUESTFS_CUSTOM_RAW_IMAGE_URL
 	depends on GUESTFS_HAS_CUSTOM_RAW_IMAGE_URL
 	string
 	output yaml
-	default "https://cloud.debian.org/images/cloud/trixie/daily/latest/debian-13-generic-amd64-daily.raw" if GUESTFS_DEBIAN_TRIXIE_GENERIC_AMD64
-	default "https://cloud.debian.org/images/cloud/trixie/daily/latest/debian-13-genericcloud-amd64-daily.raw" if GUESTFS_DEBIAN_TRIXIE_GENERIC_CLOUD_AMD64
-	default "https://cloud.debian.org/images/cloud/trixie/daily/latest/debian-13-nocloud-amd64-daily.raw" if GUESTFS_DEBIAN_TRIXIE_NOCLOUD_AMD64
+	default "https://cloud.debian.org/images/cloud/trixie/daily/latest/debian-13-generic-amd64-daily.raw" if GUESTFS_DEBIAN_TRIXIE_GENERIC_AMD64 && TARGET_ARCH_X86_64
+	default "https://cloud.debian.org/images/cloud/trixie/daily/latest/debian-13-generic-arm64-daily.raw" if GUESTFS_DEBIAN_TRIXIE_GENERIC_AMD64 && TARGET_ARCH_ARM64
+	default "https://cloud.debian.org/images/cloud/trixie/daily/latest/debian-13-genericcloud-amd64-daily.raw" if GUESTFS_DEBIAN_TRIXIE_GENERIC_CLOUD_AMD64 && TARGET_ARCH_X86_64
+	default "https://cloud.debian.org/images/cloud/trixie/daily/latest/debian-13-genericcloud-arm64-daily.raw" if GUESTFS_DEBIAN_TRIXIE_GENERIC_CLOUD_AMD64 && TARGET_ARCH_ARM64
+	default "https://cloud.debian.org/images/cloud/trixie/daily/latest/debian-13-nocloud-amd64-daily.raw" if GUESTFS_DEBIAN_TRIXIE_NOCLOUD_AMD64 && TARGET_ARCH_X86_64
+	default "https://cloud.debian.org/images/cloud/trixie/daily/latest/debian-13-nocloud-arm64-daily.raw" if GUESTFS_DEBIAN_TRIXIE_NOCLOUD_AMD64 && TARGET_ARCH_ARM64
 
 config GUESTFS_HAS_CUSTOM_RAW_IMAGE_SHA512SUMS
 	bool
@@ -177,9 +180,12 @@ config GUESTFS_DEBIAN_HOST_GUEST_MATCH
 
 config GUESTFS_DEBIAN_IMAGE_NAME
 	string
-	default "debian-13-generic-amd64-daily" if GUESTFS_DEBIAN_TRIXIE_GENERIC_AMD64
-	default "debian-13-genericcloud-amd64-daily" if GUESTFS_DEBIAN_TRIXIE_GENERIC_CLOUD_AMD64
-	default "debian-13-nocloud-amd64-daily" if GUESTFS_DEBIAN_TRIXIE_NOCLOUD_AMD64
+	default "debian-13-generic-amd64-daily" if GUESTFS_DEBIAN_TRIXIE_GENERIC_AMD64 && TARGET_ARCH_X86_64
+	default "debian-13-generic-arm64-daily" if GUESTFS_DEBIAN_TRIXIE_GENERIC_AMD64 && TARGET_ARCH_ARM64
+	default "debian-13-genericcloud-amd64-daily" if GUESTFS_DEBIAN_TRIXIE_GENERIC_CLOUD_AMD64 && TARGET_ARCH_X86_64
+	default "debian-13-genericcloud-arm64-daily" if GUESTFS_DEBIAN_TRIXIE_GENERIC_CLOUD_AMD64 && TARGET_ARCH_ARM64
+	default "debian-13-nocloud-amd64-daily" if GUESTFS_DEBIAN_TRIXIE_NOCLOUD_AMD64 && TARGET_ARCH_X86_64
+	default "debian-13-nocloud-arm64-daily" if GUESTFS_DEBIAN_TRIXIE_NOCLOUD_AMD64 && TARGET_ARCH_ARM64
 	default "debian-12" if GUESTFS_DEBIAN_BUSTER
 
 config GUESTFS_DEBIAN_BOX_SHORT
diff --git a/kconfigs/Kconfig.libvirt b/kconfigs/Kconfig.libvirt
index 961276cc..25cb703f 100644
--- a/kconfigs/Kconfig.libvirt
+++ b/kconfigs/Kconfig.libvirt
@@ -287,8 +287,12 @@ endif # QEMU_BUILD
 
 config QEMU_BIN_PATH_LIBVIRT
 	string "QEMU binary path to use"
-	default "/usr/local/bin/qemu-system-x86_64" if QEMU_USE_DEVELOPMENT_VERSION
-	default "/usr/bin/qemu-system-x86_64" if !QEMU_USE_DEVELOPMENT_VERSION
+	default "/usr/local/bin/qemu-system-x86_64" if QEMU_USE_DEVELOPMENT_VERSION && TARGET_ARCH_X86_64
+	default "/usr/local/bin/qemu-system-aarch64" if QEMU_USE_DEVELOPMENT_VERSION && TARGET_ARCH_ARM64
+	default "/usr/local/bin/qemu-system-ppc64le" if QEMU_USE_DEVELOPMENT_VERSION && TARGET_ARCH_PPC64LE
+	default "/usr/bin/qemu-system-x86_64" if !QEMU_USE_DEVELOPMENT_VERSION && TARGET_ARCH_X86_64
+	default "/usr/bin/qemu-system-aarch64" if !QEMU_USE_DEVELOPMENT_VERSION && TARGET_ARCH_ARM64
+	default "/usr/bin/qemu-system-ppc64le" if !QEMU_USE_DEVELOPMENT_VERSION && TARGET_ARCH_PPC64LE
 
 config QEMU_INSTALL_DIR_LIBVIRT
 	string "Path to install QEMU"
@@ -536,6 +540,12 @@ config LIBVIRT_MACHINE_TYPE_VIRT
 
 endchoice
 
+config LIBVIRT_MACHINE_TYPE_STRING
+	string
+	output yaml
+	default "q35" if LIBVIRT_MACHINE_TYPE_Q35
+	default "virt" if LIBVIRT_MACHINE_TYPE_VIRT
+
 config LIBVIRT_HOST_PASSTHROUGH
 	bool "Use CPU host-passthrough"
 	default y
diff --git a/kconfigs/arch/Kconfig b/kconfigs/arch/Kconfig
index 4c0beaf7..cd049f7c 100644
--- a/kconfigs/arch/Kconfig
+++ b/kconfigs/arch/Kconfig
@@ -11,6 +11,7 @@ choice
 
 config TARGET_ARCH_X86_64
 	bool "x86_64"
+	output yaml
 	help
 	  Enabling this will enable all features / workflows which depend on
 	  the x86_64 architecture.
@@ -18,6 +19,7 @@ config TARGET_ARCH_X86_64
 config TARGET_ARCH_ARM64
 	bool "arm64"
 	select HAVE_ARCH_64K_PAGES
+	output yaml
 	help
 	  Enabling this will enable all features / workflows which depend on
 	  the arm64 architecture.
@@ -25,6 +27,7 @@ config TARGET_ARCH_ARM64
 config TARGET_ARCH_PPC64LE
 	bool "ppc64le"
 	select HAVE_ARCH_64K_PAGES
+	output yaml
 	help
 	  Enabling this will enable all features / workflows which depend on
 	  the ppc64le architecture.
diff --git a/playbooks/roles/base_image/tasks/custom-image.yml b/playbooks/roles/base_image/tasks/custom-image.yml
index 204e3099..bcf35933 100644
--- a/playbooks/roles/base_image/tasks/custom-image.yml
+++ b/playbooks/roles/base_image/tasks/custom-image.yml
@@ -172,7 +172,7 @@
       when:
         - not sentinel_stat.stat.exists
 
-    - name: Customize the downloaded image with kdevops user and settings
+    - name: Customize the downloaded image with kdevops user and settings (same architecture)
       become: true
       become_method: ansible.builtin.sudo
       ansible.builtin.command:
@@ -185,12 +185,33 @@
       when:
         - libvirt_uri_system|bool
         - not sentinel_stat.stat.exists
+        - not (target_arch_arm64|default(false) and ansible_facts['architecture'] != 'aarch64')
+        - not (target_arch_x86_64|default(false) and ansible_facts['architecture'] == 'aarch64')
+
+    - name: Customize the downloaded image with kdevops user and settings (cross-architecture using firstboot only)
+      become: true
+      become_method: ansible.builtin.sudo
+      ansible.builtin.command:
+        argv:
+          - "virt-customize"
+          - "-a"
+          - "{{ custom_image }}"
+          - "--firstboot-command"
+          - "systemctl stop ssh && systemctl start ssh"
+          - "--firstboot-command"
+          - "apt update && apt upgrade --yes || yum update -y || true"
+      when:
+        - libvirt_uri_system|bool
+        - not sentinel_stat.stat.exists
+        - >-
+          (target_arch_arm64|default(false) and ansible_facts['architecture'] != 'aarch64') or
+          (target_arch_x86_64|default(false) and ansible_facts['architecture'] == 'aarch64')
       vars:
         ansible_callback_diy_runner_on_ok_msg: |
           $ {{ ansible_callback_diy.result.output.cmd | join(' ') }}
           {{ ansible_callback_diy.result.output.stdout | default('') }}
 
-    - name: Customize the downloaded image with kdevops user and settings (non-root)
+    - name: Customize the downloaded image with kdevops user and settings (non-root, same architecture)
       ansible.builtin.command:
         argv:
           - "virt-customize"
@@ -201,6 +222,25 @@
       when:
         - not libvirt_uri_system|bool
         - not sentinel_stat.stat.exists
+        - not (target_arch_arm64|default(false) and ansible_facts['architecture'] != 'aarch64')
+        - not (target_arch_x86_64|default(false) and ansible_facts['architecture'] == 'aarch64')
+
+    - name: Customize the downloaded image with kdevops user and settings (non-root, cross-architecture)
+      ansible.builtin.command:
+        argv:
+          - "virt-customize"
+          - "-a"
+          - "{{ custom_image }}"
+          - "--firstboot-command"
+          - "systemctl stop ssh && systemctl start ssh"
+          - "--firstboot-command"
+          - "apt update && apt upgrade --yes || yum update -y || true"
+      when:
+        - not libvirt_uri_system|bool
+        - not sentinel_stat.stat.exists
+        - >-
+          (target_arch_arm64|default(false) and ansible_facts['architecture'] != 'aarch64') or
+          (target_arch_x86_64|default(false) and ansible_facts['architecture'] == 'aarch64')
       vars:
         ansible_callback_diy_runner_on_ok_msg: |
           $ {{ ansible_callback_diy.result.output.cmd | join(' ') }}
diff --git a/playbooks/roles/gen_nodes/tasks/main.yml b/playbooks/roles/gen_nodes/tasks/main.yml
index 60e7f694..c3397a7d 100644
--- a/playbooks/roles/gen_nodes/tasks/main.yml
+++ b/playbooks/roles/gen_nodes/tasks/main.yml
@@ -993,7 +993,7 @@
     hostname: "{{ item.name }}"
     guestidx: "{{ idx }}"
   ansible.builtin.template:
-    src: "guestfs_{{ libvirt_machine_type }}.j2.xml"
+    src: "guestfs_{{ libvirt_machine_type_string }}.j2.xml"
     dest: "{{ topdir_path }}/guestfs/{{ hostname }}/{{ hostname }}.xml"
     force: true
     mode: "0644"
diff --git a/playbooks/roles/gen_nodes/templates/guestfs_virt.j2.xml b/playbooks/roles/gen_nodes/templates/guestfs_virt.j2.xml
index 29818ea8..e57e6fc7 100644
--- a/playbooks/roles/gen_nodes/templates/guestfs_virt.j2.xml
+++ b/playbooks/roles/gen_nodes/templates/guestfs_virt.j2.xml
@@ -4,12 +4,12 @@
   <currentMemory unit='MiB'>{{ libvirt_mem_mb }}</currentMemory>
   <vcpu placement='static'>{{ libvirt_vcpus_count }}</vcpu>
   <os firmware='efi'>
-    <type arch='aarch64' machine='virt-8.1'>hvm</type>
+    <type arch='aarch64' machine='virt'>hvm</type>
     <firmware>
       <feature enabled='no' name='enrolled-keys'/>
       <feature enabled='no' name='secure-boot'/>
     </firmware>
-    <loader readonly='yes' type='pflash' format='qcow2'>/usr/share/edk2/aarch64/QEMU_EFI-silent-pflash.qcow2</loader>
+    <loader readonly='yes' type='pflash'>/usr/share/AAVMF/AAVMF_CODE.fd</loader>
     <boot dev='hd'/>
   </os>
   <features>
diff --git a/scripts/gen-nodes.Makefile b/scripts/gen-nodes.Makefile
index 0be012d1..8ca11d60 100644
--- a/scripts/gen-nodes.Makefile
+++ b/scripts/gen-nodes.Makefile
@@ -13,6 +13,10 @@ ifeq (y,$(CONFIG_QEMU_BUILD))
   GEN_NODES_EXTRA_ARGS += qemu_bin_path='$(subst ",,$(CONFIG_QEMU_INSTALL_DIR_LIBVIRT))/qemu-system-x86_64'
   endif
 
+  ifeq (y,$(CONFIG_TARGET_ARCH_ARM64))
+  GEN_NODES_EXTRA_ARGS += qemu_bin_path='$(subst ",,$(CONFIG_QEMU_INSTALL_DIR_LIBVIRT))/qemu-system-aarch64'
+  endif
+
   ifeq (y,$(CONFIG_TARGET_ARCH_PPC64LE))
   GEN_NODES_EXTRA_ARGS += qemu_bin_path='$(subst ",,$(CONFIG_QEMU_INSTALL_DIR_LIBVIRT))/qemu-system-ppc64'
   endif

-- 
2.50.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.