diff --git a/BUILD.md b/BUILD.md index bc917b0..553ad3b 100644 --- a/BUILD.md +++ b/BUILD.md @@ -387,6 +387,14 @@ Because of this, to build `ppc64le` artifacts you must run `mockbuild-all.pl` on In short: build `ppc64le` packages on a Power machine. +`aarch64` works the same way, natively on an aarch64 host against EPEL, for EL10 only: the +manifest has an `[alma+epel-10-aarch64]` section and no EL8/EL9 one, so pass +`--target alma+epel-10-aarch64` (a run without `--target` also tries rh8 and rh9 and stops at the +missing section). The x86 boot loaders are built there like on ppc64le: `elilo-xcat` ships its +tracked prebuilt EFI payload and `syslinux-xcat` installs the prebuilt bootloader blobs of the +release tarball, so the noarch rpms carry the same payload as the ppc64le build. aarch64 is not +part of the `--finalize-xcat-dep` cross-arch genesis copy. + # riscv64 (EL10): cross-building on an x86_64 host There is no riscv64 build host in the xCAT build farm and no EPEL for riscv64, so the diff --git a/elilo/elilo-xcat.spec b/elilo/elilo-xcat.spec index 397a815..24507af 100644 --- a/elilo/elilo-xcat.spec +++ b/elilo/elilo-xcat.spec @@ -20,11 +20,11 @@ Patch2: elilo-big-bzimage-limit.patch Patch3: elilo-gnu-efi-strncpy-conflict.patch Source4: elilo-xcat-3.14-6.noarch.rpm # Ship the tracked prebuilt EFI payload (SOURCE4) instead of compiling on targets where the -# gnu-efi toolchain layout does not support elilo's build: ppc64le (no x86 EFI toolchain) and -# EL8 (gnu-efi-devel places elf_x86_64_efi.lds under a path elilo's Makefile does not find). +# gnu-efi toolchain layout does not support elilo's build: ppc64le and aarch64 (no x86 EFI +# toolchain) and EL8 (gnu-efi-devel places elf_x86_64_efi.lds under a path elilo's Makefile does not find). # elilo-x64.efi is a noarch artifact, so the prebuilt is byte-identical to the compiled one. -# Use the tracked prebuilt on ppc (no x86 EFI toolchain) and on EL8 (gnu-efi lds path gap). -# Three SEPARATE %if blocks, each a single simple compare -- NOT one `A || B` expression (the +# Use the tracked prebuilt on ppc and aarch64 (no x86 EFI toolchain) and on EL8 (gnu-efi lds path gap). +# SEPARATE %if blocks, each a single simple compare -- NOT one `A || B` expression (the # older rpm in the EL8/EL9 mock chroot mis-evaluates `||`), and NOT %ifarch (during `rpmbuild -bs` # the srpm's BuildRequires are frozen against %{_host_cpu}, not the target). Crucially, alma ppc # chroots report %{_host_cpu}=powerpc64le while rocky reports ppc64le, so BOTH must be matched -- @@ -35,6 +35,9 @@ Source4: elilo-xcat-3.14-6.noarch.rpm %if "%{_host_cpu}" == "powerpc64le" %global use_prebuilt 1 %endif +%if "%{_host_cpu}" == "aarch64" +%global use_prebuilt 1 +%endif %if 0%{?rhel} == 8 %global use_prebuilt 1 %endif @@ -68,7 +71,7 @@ sed -i 's|^EFILIB[[:space:]]*=.*|EFILIB = /usr/lib64|' Make.defaults sed -i 's|^EFICRT0[[:space:]]*=.*|EFICRT0 = /usr/lib|' Make.defaults %endif %if 0%{?use_prebuilt} -# Reuse the prebuilt EFI payload from the tracked noarch package (ppc64le / EL8). +# Reuse the prebuilt EFI payload from the tracked noarch package (ppc64le / aarch64 / EL8). mkdir -p prebuilt rpm2cpio %{SOURCE4} | (cd prebuilt && cpio -idm --quiet) test -f prebuilt/tftpboot/xcat/elilo-x64.efi diff --git a/grub2-xcat/grub2-xcat.spec b/grub2-xcat/grub2-xcat.spec index de43168..866957a 100644 --- a/grub2-xcat/grub2-xcat.spec +++ b/grub2-xcat/grub2-xcat.spec @@ -6,7 +6,7 @@ Name: grub2-xcat Epoch: 1 Version: 1.0 #Release: snap%(date +"%Y%m%d%H%M") -Release: 3 +Release: 4 License: GPLv3+ Group: Applications/System Vendor: IBM Corp. @@ -20,6 +20,16 @@ Source1: grub2-res.tar.gz # BaseOS riscv64 tree (EFI/BOOT/grubriscv64.efi, built from # grub2-2.12-46.el10_2.rocky.0.1.src.rpm, GPL-3.0-or-later); see readme. Source2: grubriscv64.efi +# aarch64 nodes boot through UEFI and grub2 like riscv64, but the distro grubaa64.efi +# carries the distro prefix (/EFI/) and never finds the boot/grub2/grub.cfg-* +# that nodeset writes. The aarch64 image is therefore generated here with grub2-mkimage +# and the /boot/grub2 prefix grub2.ppc uses. grub2-efi-aa64-modules is noarch, so the +# image builds on every EL build host. The openEuler x86_64 repositories carry no +# grub2-efi-aa64-modules and openEuler has no aarch64 target, so it ships without the image. +%if ! 0%{?openEuler} +BuildRequires: grub2-tools +BuildRequires: grub2-efi-aa64-modules +%endif Provides: grub2-xcat Requires:/bin/sh @@ -27,7 +37,8 @@ Requires:/bin/sh %description grub2-xcat provides some grub2 resources generated by grub2-mknetdir,including grub2 modules and grub2 image, -and the EL grub2 UEFI image for riscv64 nodes, which boot through UEFI and grub2 only. +and the EL grub2 UEFI image for riscv64 nodes and an xCAT-prefixed grub2 UEFI image for +aarch64 nodes, which boot through UEFI and grub2 only. %define _binaries_in_noarch_packages_terminate_build 0 %if 0%{?openEuler} @@ -46,6 +57,15 @@ and the EL grub2 UEFI image for riscv64 nodes, which boot through UEFI and grub2 #%endif %build +%if ! 0%{?openEuler} +# The aarch64 network loader: net search finds the boot/grub2/grub.cfg- that +# nodeset links. efi_uga is left out: it is x86 only, and grub2-mkimage aborts on it +# for arm64-efi. +grub2-mkimage -O arm64-efi -p /boot/grub2 -o %{_builddir}/grubaa64.efi \ + tftp efinet net http normal linux configfile search search_fs_file search_fs_uuid \ + search_label echo test cat ls boot reboot halt help fat ext2 xfs part_gpt part_msdos \ + gzio all_video video efi_gop font gettext terminal terminfo loadenv read sleep +%endif %pre @@ -63,6 +83,10 @@ tar -xf grub2-res.tar %endif mkdir -p $RPM_BUILD_ROOT/%{prefix}/riscv64-efi install -m 0644 %{SOURCE2} $RPM_BUILD_ROOT/%{prefix}/riscv64-efi/grubriscv64.efi +%if ! 0%{?openEuler} +mkdir -p $RPM_BUILD_ROOT/%{prefix}/aarch64-efi +install -m 0644 %{_builddir}/grubaa64.efi $RPM_BUILD_ROOT/%{prefix}/aarch64-efi/grubaa64.efi +%endif @@ -72,6 +96,11 @@ cp %{prefix}/powerpc-ieee1275/core.elf %{prefix}/grub2.ppc # riscv64 firmware loads boot/grub2/grub2.riscv64 (DHCP client architecture 27) rm -f %{prefix}/grub2.riscv64 cp %{prefix}/riscv64-efi/grubriscv64.efi %{prefix}/grub2.riscv64 +# aarch64 firmware loads boot/grub2/grub2.aarch64 (DHCP client architecture 11) +rm -f %{prefix}/grub2.aarch64 +if [ -f %{prefix}/aarch64-efi/grubaa64.efi ]; then + cp %{prefix}/aarch64-efi/grubaa64.efi %{prefix}/grub2.aarch64 +fi exit 0 %clean @@ -82,6 +111,9 @@ exit 0 # one for sles, one for rhel. yes, it's ugly... %{prefix}/powerpc-ieee1275/ %{prefix}/riscv64-efi/ +%if ! 0%{?openEuler} +%{prefix}/aarch64-efi/ +%endif %defattr(-,root,root) %postun @@ -89,5 +121,6 @@ exit 0 if [ "$1" = "0" ]; then rm %{prefix}/grub2.ppc rm -f %{prefix}/grub2.riscv64 + rm -f %{prefix}/grub2.aarch64 fi true diff --git a/grub2-xcat/mockbuild.pl b/grub2-xcat/mockbuild.pl index 8835fd4..670cde8 100755 --- a/grub2-xcat/mockbuild.pl +++ b/grub2-xcat/mockbuild.pl @@ -41,7 +41,7 @@ die "Mode regenerate-from-el10-srcrpm is not supported in this script yet; use r make_path($recompile_dir) if !-d $recompile_dir; -for my $bin (qw(mock rpmbuild rpm dnf file bash tar grep cmp)) { +for my $bin (qw(mock rpmbuild rpm rpm2cpio cpio dnf file bash tar grep cmp)) { run("command -v " . sh_quote($bin) . " >/dev/null 2>&1"); } @@ -208,6 +208,24 @@ run( "rpm -qpl " . sh_quote($main_rpm) . " | grep -Fx /tftpboot/boot/grub2/riscv64-efi/grubriscv64.efi >/dev/null" ); +# The aarch64 image is generated in %build (grub2-mkimage -O arm64-efi); openEuler ships without it. +if ($mock_cfg !~ /^openeuler-/) { + run( + "rpm -qpl " . sh_quote($main_rpm) . + " | grep -Fx /tftpboot/boot/grub2/aarch64-efi/grubaa64.efi >/dev/null" + ); + my $extract_dir = "$work_dir/payload"; + remove_tree($extract_dir) if -d $extract_dir; + make_path($extract_dir); + run( + "cd " . sh_quote($extract_dir) . " && rpm2cpio " . sh_quote($main_rpm) . + " | cpio -idm --quiet ./tftpboot/boot/grub2/aarch64-efi/grubaa64.efi" + ); + my $aa64_efi = "$extract_dir/tftpboot/boot/grub2/aarch64-efi/grubaa64.efi"; + my $aa64_efi_type = pe_image_type($aa64_efi); + die "Unexpected aarch64 grub2 image type: $aa64_efi is $aa64_efi_type, expected PE32+ Aarch64\n" + if $aa64_efi_type ne 'PE32+ Aarch64'; +} print "Verified RPM name/arch/payload: $main_rpm\n"; print_step("Copy artifacts and logs"); diff --git a/grub2-xcat/readme b/grub2-xcat/readme index 0674598..54fb009 100644 --- a/grub2-xcat/readme +++ b/grub2-xcat/readme @@ -34,3 +34,20 @@ copied to /tftpboot/boot/grub2/grub2.riscv64 by the package scripts, which is the boot file xCAT hands to DHCP client architecture 27 (0x001b). To refresh it, take EFI/BOOT/grubriscv64.efi from the BaseOS tree of the EL10 release you target and record the release, source rpm and sha256 here. + +#aarch64: +aarch64 nodes boot through UEFI firmware and grub2 only, like riscv64, but +the distro grubaa64.efi cannot be shipped as is: its embedded prefix points +at the distro's /EFI/ directory, so it never finds the +boot/grub2/grub.cfg- that nodeset writes. The image is therefore +generated in %build with + + grub2-mkimage -O arm64-efi -p /boot/grub2 -o grubaa64.efi + +(BuildRequires grub2-tools and the noarch grub2-efi-aa64-modules), the same +/boot/grub2 prefix the grub2.ppc core image uses. efi_uga is not in the +module list: it is x86 only and grub2-mkimage aborts on it for arm64-efi. +It is installed as /tftpboot/boot/grub2/aarch64-efi/grubaa64.efi and +copied to /tftpboot/boot/grub2/grub2.aarch64 by %post, which is the boot +file xCAT hands to DHCP client architecture 11 (0x000b). The openEuler +build carries no grub2-efi-aa64-modules and ships without the image. diff --git a/packages-manifest.conf b/packages-manifest.conf index 565d0cf..ccff757 100644 --- a/packages-manifest.conf +++ b/packages-manifest.conf @@ -187,6 +187,35 @@ perl-Net-Telnet=3.04 perl-Sys-Virt=11.10.0 xCAT-genesis-base=>= 2:2.18.0 +# alma+epel-10-aarch64 is a native EPEL target like x86_64 and ppc64le, built on an aarch64 host +# (not a forcearch cross-build like riscv64), so it lists what [alma+epel-10-x86_64] lists. The +# x86 boot loaders (elilo-xcat, ipxe-xcat, syslinux-xcat, xnba-undi) are noarch and are listed like +# on ppc64le: an aarch64 management node serves the x86 nodes of a mixed cluster. aarch64 nodes +# themselves boot UEFI and grub2, from the grub2.aarch64 image grub2-xcat generates. +[alma+epel-10-aarch64] +conserver-xcat=8.2.1 +elilo-xcat=3.14 +goconserver=>= 0.3.3-snap202011021058 +grub2-xcat=1.0 +ipmitool-xcat=>= 1.8.18-4 +ipxe-xcat=2.0.0 +syslinux-xcat=>= 6.03-1 +xnba-undi=>= 1.21.1-1 +perl-Crypt-CBC=2.33 +perl-Crypt-Rijndael=1.13 +perl-Crypt-SSLeay=0.72 +perl-Digest-SHA1=2.13 +perl-Expect=1.35 +perl-HTML-Form=6.07 +perl-HTTP-Async=>= 0.30-3 +perl-IO-Stty=>= 0.04-5 +perl-IO-Tty=1.20 +perl-Net-DNS=1.57 +perl-Net-HTTPS-NB=>= 0.14-3 +perl-Net-Telnet=3.04 +perl-Sys-Virt=11.10.0 +xCAT-genesis-base=>= 2:2.18.0 + # rocky-10-riscv64-xcat is the forcearch (cross-built) EL10 riscv64 target. It differs from the # EPEL-fed EL10 sections above in one way (see BUILD.md, "riscv64"): riscv64 has no EPEL, so it # also builds perl-Crypt-Blowfish, perl-Mail-Sender, perl-Net-IP and perl-Path-Class. A management diff --git a/syslinux/syslinux-xcat.spec b/syslinux/syslinux-xcat.spec index ec29645..d08da9d 100644 --- a/syslinux/syslinux-xcat.spec +++ b/syslinux/syslinux-xcat.spec @@ -16,7 +16,7 @@ License: GPL Group: System/Boot Source0: https://www.kernel.org/pub/linux/utils/boot/syslinux/%{name}-%{VERSION}.tar.xz Patch0: syslinux-6.03-multibootif.patch -ExclusiveArch: i386 i486 i586 i686 athlon pentium4 x86_64 ppc64le +ExclusiveArch: i386 i486 i586 i686 athlon pentium4 x86_64 ppc64le aarch64 Packager: H. Peter Anvin Buildroot: %{_tmppath}/%{name}-%{VERSION}-root Autoreq: 0 @@ -30,7 +30,7 @@ BuildRequires: libuuid-devel # a system bootloader, and may be necessary for system recovery. %define _sbindir /sbin -%ifnarch ppc64le +%ifnarch ppc64le aarch64 %package devel Summary: Development environment for SYSLINUX add-on modules Group: Development/Libraries @@ -43,7 +43,7 @@ filesystems, Linux ext2/ext3 filesystems (EXTLINUX), PXE network boots (PXELINUX), or ISO 9660 CD-ROMs (ISOLINUX). It also includes a tool, MEMDISK, which loads legacy operating systems from these media. -%ifnarch ppc64le +%ifnarch ppc64le aarch64 %description devel The SYSLINUX boot loader contains an API, called COM32, for writing sophisticated add-on modules. This package contains the libraries @@ -88,7 +88,7 @@ make CC='%{my_cc}' PYTHON=python3 installer %install rm -rf %{buildroot} -%ifarch ppc64le +%ifarch ppc64le aarch64 make CC='%{my_cc}' PYTHON=python3 install \ INSTALLROOT=%{buildroot} BINDIR=%{_bindir} SBINDIR=%{_sbindir} \ LIBDIR=%{_libdir} DATADIR=%{_datadir} \ @@ -126,14 +126,14 @@ rm -rf %{buildroot} %{_datadir}/syslinux/*.bin %{_datadir}/syslinux/*.0 %{_datadir}/syslinux/memdisk -%ifnarch ppc64le +%ifnarch ppc64le aarch64 %{_datadir}/syslinux/dosutil/* %endif %{_datadir}/syslinux/diag/* %{_datadir}/syslinux/efi32 %{_datadir}/syslinux/efi64 -%ifnarch ppc64le +%ifnarch ppc64le aarch64 %files devel %{_datadir}/syslinux/com32 %endif diff --git a/t/aarch64_target.t b/t/aarch64_target.t new file mode 100644 index 0000000..b5d89ab --- /dev/null +++ b/t/aarch64_target.t @@ -0,0 +1,170 @@ +#!/usr/bin/env perl +# The EL10 aarch64 target: its manifest cell, the x86 boot loaders it builds natively like ppc64le, +# and the grub2.aarch64 network loader grub2-xcat generates for aarch64 nodes. +# +# alma+epel-10-aarch64 is a native EPEL target, so it must ship what the EL10 x86_64 cell ships: a +# service node perl package missing there would leave `dnf install xCATsn` unresolvable on aarch64 +# only. aarch64 nodes boot through UEFI and grub2, and xCAT hands them boot/grub2/grub2.aarch64. +# The distro grubaa64.efi cannot be that file (its prefix is the distro's /EFI/), so the +# spec generates one with grub2-mkimage and the /boot/grub2 prefix; efi_uga must stay out of the +# module list, because it is x86 only and grub2-mkimage aborts on it for arm64-efi. +use strict; +use warnings; + +use File::Temp qw(tempdir); +use FindBin qw($RealBin); +use Test::More; + +use lib $RealBin, "$RealBin/.."; +use MockBuildUtils qw(read_manifest); + +my $root = "$RealBin/.."; +my $target = 'alma+epel-10-aarch64'; + +sub slurp { + my ($path) = @_; + open(my $fh, '<', $path) or die "read $path: $!"; + local $/; + return scalar <$fh>; +} + +# The body of one spec section (%build, %install, %post, ...), up to the next section. +sub section { + my ($spec, $name) = @_; + my ($body) = $spec =~ /^%\Q$name\E[ \t]*\n(.*?)(?=^%(?:prep|build|install|check|clean|files|pre|post|preun|postun|changelog|description|package)\b|\z)/ms; + return $body // ''; +} + +# The grub2-mkimage invocation of a spec text, joined across its continuation lines. +sub mkimage_command { + my ($text) = @_; + my ($cmd) = $text =~ /^(grub2-mkimage\b(?:[^\n]*\\\n)*[^\n]*)$/m; + return unless defined $cmd; + $cmd =~ s/\\\n/ /g; + return $cmd; +} + +# rpmspec -P of a spec under extra macro definitions, or undef when rpmspec is unavailable. +my $have_rpmspec = system('command -v rpmspec >/dev/null 2>&1') == 0; +sub parsed_spec { + my ($spec, %define) = @_; + return unless $have_rpmspec; + my @args = map { ('--define', "$_ $define{$_}") } sort keys %define; + # rpmspec warns about the specs' old %patchN and macros in comments; keep that out of TAP. + open(my $save, '>&', \*STDERR) or die "dup STDERR: $!"; + open(STDERR, '>', '/dev/null') or die "mute STDERR: $!"; + my $out; + if (open(my $fh, '-|', 'rpmspec', '-P', @args, $spec)) { + local $/; + $out = <$fh>; + $out = undef unless close($fh); + } + open(STDERR, '>&', $save) or die "restore STDERR: $!"; + return $out; +} + +# ---- the manifest cell --------------------------------------------------------------------------- +{ + my %m = read_manifest("$root/packages-manifest.conf"); + my $cell = $m{$target}; + ok($cell, "the manifest has a [$target] section") or $cell = {}; + + # A native EPEL target: the same packages at the same pins as EL10 x86_64, the service node + # perl set (perl-Crypt-CBC ... perl-Net-DNS) and xCAT-genesis-base included. + is_deeply($cell, $m{'alma+epel-10-x86_64'}, + "[$target] lists what [alma+epel-10-x86_64] lists, at the same pins"); + + # The x86 boot loaders are noarch and an aarch64 management node serves the x86 nodes of a mixed + # cluster, as a ppc64le one does. + for my $boot (qw(elilo-xcat grub2-xcat ipxe-xcat syslinux-xcat xnba-undi)) { + is($cell->{$boot}, $m{'alma+epel-10-ppc64le'}{$boot}, + "$boot pinned in [$target] as in the EL10 ppc64le target"); + } +} + +# ---- grub2-xcat: generate, ship and install grub2.aarch64 -------------------------------------- +my $grub_spec_file = "$root/grub2-xcat/grub2-xcat.spec"; +my $grub_spec = slurp($grub_spec_file); +{ + like($grub_spec, qr/^BuildRequires:\s*grub2-tools\s*$/m, 'grub2-xcat BuildRequires grub2-tools'); + like($grub_spec, qr/^BuildRequires:\s*grub2-efi-aa64-modules\s*$/m, + 'grub2-xcat BuildRequires the noarch arm64-efi grub modules'); + + my $cmd = mkimage_command(section($grub_spec, 'build')); + ok(defined $cmd, '%build runs grub2-mkimage') or $cmd = ''; + like($cmd, qr/(?:^|\s)-O\s+arm64-efi(?:\s|$)/, '... for the arm64-efi platform'); + like($cmd, qr/(?:^|\s)-p\s+\/boot\/grub2(?:\s|$)/, '... with the /boot/grub2 prefix grub2.ppc uses'); + like($cmd, qr/(?:^|\s)-o\s+\S*grubaa64\.efi(?:\s|$)/, '... into grubaa64.efi'); + my @modules = split ' ', ($cmd =~ /grubaa64\.efi\s+(.*)$/)[0] // ''; + for my $mod (qw(tftp efinet net http normal linux configfile search)) { + ok((grep { $_ eq $mod } @modules), "... with the $mod module"); + } + ok(!(grep { $_ eq 'efi_uga' } @modules), '... and without efi_uga (x86 only)'); + + like(section($grub_spec, 'install'), + qr{^install -m 0644 \S*grubaa64\.efi \$RPM_BUILD_ROOT/%\{prefix\}/aarch64-efi/grubaa64\.efi$}m, + '%install ships it as aarch64-efi/grubaa64.efi'); + like(section($grub_spec, 'files'), qr{^%\{prefix\}/aarch64-efi/$}m, '%files owns aarch64-efi/'); + like(section($grub_spec, 'post'), + qr{^\s*cp\s+%\{prefix\}/aarch64-efi/grubaa64\.efi %\{prefix\}/grub2\.aarch64$}m, + '%post installs it as grub2.aarch64'); + like(section($grub_spec, 'postun'), qr{^\s*rm -f %\{prefix\}/grub2\.aarch64$}m, + '%postun removes grub2.aarch64'); + + SKIP: { + skip 'rpmspec not available', 4 unless $have_rpmspec; + my $el = parsed_spec($grub_spec_file, rhel => 10); + ok(defined $el, 'rpmspec parses grub2-xcat.spec for EL10') or $el = ''; + like($el, qr/^BuildRequires:\s*grub2-efi-aa64-modules\s*$/m, '... and EL keeps the aarch64 image'); + + # openEuler's x86_64 repositories carry no grub2-efi-aa64-modules: there the image is + # neither required nor shipped, and %post must not copy a file that is not there. + my $oe = parsed_spec($grub_spec_file, openEuler => 1); + ok(defined $oe && $oe !~ /grub2-efi-aa64-modules|grub2-mkimage|aarch64-efi\/$/m, + 'openEuler builds without the aarch64 image'); + like($oe // '', qr{if \[ -f /tftpboot/boot/grub2/aarch64-efi/grubaa64\.efi \]}, + '... and its %post copies grub2.aarch64 only when the image exists'); + } + + # Run the spec's own command where the arm64-efi modules are installed: a module the grub + # build does not carry, or efi_uga, makes grub2-mkimage fail. + SKIP: { + my $moddir = '/usr/lib/grub/arm64-efi'; + skip 'grub2-mkimage or the arm64-efi grub modules are not installed', 4 + unless -d $moddir && system('command -v grub2-mkimage >/dev/null 2>&1') == 0; + skip 'no grub2-mkimage command in the spec', 4 if $cmd eq ''; + my $tmp = tempdir(CLEANUP => 1); + my $run = $cmd; + $run =~ s/(?<=\s-o\s)\S+/$tmp\/grubaa64.efi/; + is(system("$run >/dev/null 2>&1"), 0, "the spec's grub2-mkimage command succeeds"); + my $image = ''; + if (open(my $fh, '<:raw', "$tmp/grubaa64.efi")) { local $/; $image = <$fh> // ''; } + my $pe = length($image) > 0x40 ? unpack('V', substr($image, 0x3c, 4)) : 0; + is(length($image) > $pe + 6 ? unpack('v', substr($image, $pe + 4, 2)) : 0, 0xaa64, + '... and writes an AArch64 PE image'); + like($image, qr{/boot/grub2\0}, '... whose embedded prefix is /boot/grub2'); + isnt(system("$run efi_uga >/dev/null 2>&1"), 0, 'adding efi_uga makes the command fail'); + } +} + +# ---- the x86 boot loaders build natively on aarch64, as on ppc64le ----------------------------- +{ + my $syslinux = slurp("$root/syslinux/syslinux-xcat.spec"); + my ($exclusive) = $syslinux =~ /^ExclusiveArch:\s*(.*?)\s*$/m; + my %arches = map { $_ => 1 } split ' ', $exclusive // ''; + ok($arches{aarch64} && $arches{ppc64le}, 'syslinux ExclusiveArch admits aarch64 beside ppc64le'); + my @ppc_only = $syslinux =~ /^%ifn?arch ppc64le\s*$/mg; + is_deeply(\@ppc_only, [], 'every ppc64le arch conditional of syslinux covers aarch64 as well'); + + my $elilo_file = "$root/elilo/elilo-xcat.spec"; + SKIP: { + skip 'rpmspec not available', 2 unless $have_rpmspec; + my $aa64 = parsed_spec($elilo_file, _host_cpu => 'aarch64', rhel => 10) // ''; + unlike($aa64, qr/^BuildRequires:\s*gnu-efi/m, + 'elilo on aarch64 needs no gnu-efi (it ships the tracked prebuilt EFI payload)'); + my $x86 = parsed_spec($elilo_file, _host_cpu => 'x86_64', rhel => 10) // ''; + like($x86, qr/^BuildRequires:\s*gnu-efi/m, '... while EL10 x86_64 still compiles it'); + } +} + +done_testing;