# SPDX-License-Identifier: GPL-2.0-or-later # # Kernel SRPM for the AYANEO Pocket DS (Snapdragon 8 Gen 2 / SM8550 / qcs8550). # # Aggressively trimmed from upstream Fedora's multi-arch / multi-variant # kernel.spec: this build is aarch64-only, single-variant (stock), single # device (Pocket DS), and uses one config (rocknix-linux.aarch64.conf). # # Boot artefact: /boot/Image- — Android boot.img v0 # (ANDROID! magic + header + gzip(arch/arm64/boot/Image) ++ # qcs8550-ayaneo-pocketds.dtb + cmdline) produced by mkbootimg. A # byte-for-byte copy is maintained at /boot/Image (rpm-untracked, see # %%posttrans) -- that's the unversioned path the ROCKNIX-signed ABL # probes. Per-version Image-* files let multiple kernels coexist on # disk without rpm transaction conflicts; pre-versioned-Image releases # all claimed /boot/Image directly and dnf upgrade refused with # "file /boot/Image from install of kernel-X.Y conflicts with file # from package kernel-X.Z". # # The ABL only knows the boot.img layout (raw arm64 Image at the # same path triggers a hard reset shortly after "Booting Linux"), so # the wrapping is mandatory. # # DTBs: built from the in-tree arch/arm64/boot/dts/qcom/qcs8550-*.dts files # already present on the linux-pocketds:pocketds/v7.0 branch (the rocknix # patches are upstream of that branch — no patch application needed here). %global package_name kernel %global tarfile_release 7.0 # Don't generate kernel-debuginfo / kernel-debugsource subpackages. The kernel # is built with -g but we ship it as-is in /boot/Image; producing debuginfo # RPMs requires a -debuginfo subpackage definition with %files, which we # deliberately don't carry. Without these stubs rpmbuild's auto-debugsource # step fails with "Empty %files file debugsourcefiles.list". %global debug_package %{nil} %global _enable_debug_packages 0 %global _build_id_links none Name: %{package_name} Version: 7.0.0 # `.pocketds` suffix bumps NVR above Fedora's vanilla kernel-7.0.0-62.fc44 # so that mkosi (and dnf in general) installs our rocknix-patched + # boot.img-wrapped kernel rather than the upstream Fedora signed build. Release: 101.20260507173310.pocketds%{?dist} Summary: Linux kernel for the AYANEO Pocket DS (SM8550) License: GPL-2.0-only WITH Linux-syscall-note URL: https://gitlab.com/linux-pocketds/linux ExclusiveArch: aarch64 ExclusiveOS: Linux # Pre-patched kernel tree (rocknix patches + qcs8550-*.dts files already in tree) Source0: https://gitlab.com/linux-pocketds/linux/-/archive/pocketds/v%{tarfile_release}/linux-pocketds-v%{tarfile_release}.tar.gz#/linux-%{tarfile_release}.tar.gz Source1: rocknix-linux.aarch64.conf # Busybox initramfs source maintained in the standalone `pocketds-initramfs` # repo (https://gitlab.com/linux-pocketds/pocketds-initramfs). Carries the # init script + busybox/fsck binaries AND the AYANEO-signed Adreno 740 GPU # firmware (qcom/a740_sqe.fw, qcom/gmu_gen70200.bin, qcom/a740_zap.{mdt,bNN,mbn}) # under usr/lib/firmware/qcom/. msm.ko is built into this kernel, so it # request_firmware()s these blobs during early probe -- before the rootfs is # mounted. Bundling them inside this tarball is what makes the GPU actually # come up; without them the kernel logs "Direct firmware load for # qcom/a740_sqe.fw failed -2" and kwin_wayland later asserts and SIGABRTs. # # The tarball is produced by `pocketds-initramfs/make-tarball.sh` and is # refreshed in this dir by `fetch-sources.sh` before each COPR build. Source2: pocketds-initramfs.tar.zst BuildRequires: bash bc bison binutils diffutils elfutils-devel findutils flex BuildRequires: gawk gcc gcc-c++ gettext-devel git-core hostname kmod make BuildRequires: ncurses-devel net-tools openssl openssl-devel patch perl-interpreter BuildRequires: python3-devel rsync tar which xz zlib-devel BuildRequires: dtc BuildRequires: android-tools BuildRequires: zstd tar Requires: coreutils Requires: systemd >= 203 Requires: linux-firmware Provides: kernel-uname-r = %{version}-%{release}.%{_arch} Provides: kernel-modules = %{version}-%{release} Provides: kernel-modules-core = %{version}-%{release} Provides: kernel-modules-extra = %{version}-%{release} Provides: kernel-core = %{version}-%{release} Provides: installonlypkg(kernel) %define KernelVer %{version}-%{release}.%{_arch} %define image_install_path boot %define hdrarch arm64 %define asmarch arm64 %description The Linux kernel built for the AYANEO Pocket DS handheld. Single-variant, aarch64-only, configured from rocknix-linux.aarch64.conf. Installs an Android boot.img v0 at /boot/Image (kernel + DTB + cmdline in one ANDROID!-magic file); the ROCKNIX-signed ABL chain-loads it directly off the FAT ROCKNIX partition. %package devel Summary: Development files for building modules against the Pocket DS kernel Provides: kernel-devel = %{version}-%{release} Provides: kernel-devel-uname-r = %{KernelVer} Requires: kernel-uname-r = %{KernelVer} %description devel Headers, Makefiles, and scripts for building external modules against the Pocket DS kernel. # ---------------------------------------------------------------- prep ---- %prep %setup -q -n linux-pocketds-v%{tarfile_release} # Drop the .config in place cp %{SOURCE1} .config # Extract the standalone pocketds-initramfs payload to the kernel build dir # so CONFIG_INITRAMFS_SOURCE can point at it. The tarball ships a top-level # `initramfs/` dir (with usr/lib/firmware/qcom/ already populated); we land # at $PWD/initramfs/. tar --zstd -xf %{SOURCE2} [ -d initramfs ] || { echo "ERROR: initramfs/ missing after extract"; exit 1; } [ -f initramfs/usr/lib/firmware/qcom/a740_sqe.fw ] || { echo "ERROR: %{SOURCE2} is missing usr/lib/firmware/qcom/a740_sqe.fw -- "\ "bump pocketds-initramfs and re-run fetch-sources.sh" >&2 exit 1 } # Substitute ROCKNIX build-system placeholders. INITRAMFS_SOURCE points # at the absolute path of the tree we just extracted — Kbuild's # usr/Makefile uses gen_initramfs.sh to walk that directory and emit # usr/initramfs_data.cpio, which gets embedded as a section in the # final Image. INITRAMFS_DIR="$(pwd)/initramfs" sed -i \ -e "s|\"@INITRAMFS_SOURCE@\"|\"${INITRAMFS_DIR}\"|" \ -e 's|"@DEVICENAME@"|"pocketds"|' \ .config # Fix Makefile typo in upstream linux-pocketds tree: `dtb-y` entries must # reference the *compiled* .dtb name, not the .dts source. With `.dts` the # build invokes dtc but `dtbs_install` copies the source instead of the # compiled blob. sed -i 's|qcs8550-ayaneo-pocketds\.dts$|qcs8550-ayaneo-pocketds.dtb|' \ arch/arm64/boot/dts/qcom/Makefile # Make sure setlocalversion can't append a +/-dirty suffix. touch .scmversion rm -f localversion-next localversion-rt # --------------------------------------------------------------- build ---- %build make ARCH=%{asmarch} olddefconfig # Pin the build salt so module signatures and modules.builtin reference KernelVer perl -p -i -e "s/^CONFIG_BUILD_SALT.*/CONFIG_BUILD_SALT=\"%{KernelVer}\"/" .config make %{?_smp_mflags} ARCH=%{asmarch} \ KCFLAGS="$KCFLAGS" \ KERNELRELEASE=%{KernelVer} \ Image modules dtbs # ------------------------------------------------------------- install ---- %install rm -rf $RPM_BUILD_ROOT mkdir -p $RPM_BUILD_ROOT/boot mkdir -p $RPM_BUILD_ROOT/lib/modules/%{KernelVer} # Raw kernel kept under /lib/modules//Image for diagnostics and # recovery flows that want an unwrapped Image matching the module tree. install -m 0644 arch/arm64/boot/Image $RPM_BUILD_ROOT/lib/modules/%{KernelVer}/Image # /boot/Image — Android boot.img v0. ROCKNIX-signed ABL parses the # ANDROID! header, gunzips the kernel section to get arm64 Image, scans # the residual bytes (after the gzip stream) for FDT magic to find our # appended DTB, patches /chosen/bootargs with the cmdline carried in # the header (merged with its own androidboot.* tokens), and jumps. # # Earlycon is in the cmdline so any kernel panic before display init # at least reaches the SoC's debug UART (which the AYANEO chassis # might or might not expose — without a serial bring-out cable we # don't see it, but the kernel logs it). _abltmp=$(mktemp -d) # Match ROCKNIX's image-builder mkbootimg invocation byte-for-byte: # gzipped kernel + appended DTB, all offsets zero (mkbootimg's default # --base 0x10000000 — irrelevant on this ABL which uses platform-fixed # load addresses), header_version 0, cmdline using boot=LABEL/disk=LABEL. # A real (non-empty) ramdisk is mandatory: ABL writes # linux,initrd-start/initrd-end into /chosen for the kernel and a # 5-byte "dummy" trips up FDT path-walking on some ABL revisions. gzip -c arch/arm64/boot/Image > ${_abltmp}/kernel.gz cat arch/arm64/boot/dts/qcom/qcs8550-ayaneo-pocketds.dtb \ >> ${_abltmp}/kernel.gz # Empty-file ramdisk → mkbootimg writes ramdisk_size=0 in the boot.img # header and skips writing a ramdisk section. ROCKNIX's working KERNEL # is shaped this way: ABL never writes linux,initrd-start/end into # /chosen, so the kernel doesn't try to find an initramfs. Anything # non-empty here (including a 512-byte empty cpio archive) makes ABL # point /chosen/initrd-start at random memory, which the kernel then # mis-parses as initramfs and watchdog-resets. : > ${_abltmp}/ramdisk mkbootimg \ --kernel ${_abltmp}/kernel.gz \ --ramdisk ${_abltmp}/ramdisk \ --kernel_offset 0x00000000 \ --ramdisk_offset 0x00000000 \ --tags_offset 0x00000000 \ --os_version 12.0.0 \ --os_patch_level "$(date '+%%Y-%%m')" \ --header_version 0 \ --cmdline "rdinit=/init root=PARTLABEL=STORAGE rw boot=LABEL=ROCKNIX disk=LABEL=STORAGE quiet rootwait console=tty0 allow_mismatched_32bit_el0 fw_devlink.strict=1 pcie_ports=compat irqaffinity=0-2 cgroup.memory=nokmem,nosocket nosoftlockup usbcore.interrupt_interval_override=045e:028e:2" \ -o $RPM_BUILD_ROOT/boot/Image-%{KernelVer} rm -rf ${_abltmp} # Per-version filename so multiple kernels coexist (rpm refused # transactions when both kernels claimed /boot/Image). The unversioned # /boot/Image symlink that the ROCKNIX-signed ABL reads is created / # refreshed in %posttrans below; rpm doesn't track it, so no conflict. # Config + System.map — diagnostic + module dep resolution install -m 0644 .config $RPM_BUILD_ROOT/boot/config-%{KernelVer} install -m 0644 .config $RPM_BUILD_ROOT/lib/modules/%{KernelVer}/config install -m 0644 System.map $RPM_BUILD_ROOT/boot/System.map-%{KernelVer} install -m 0644 System.map $RPM_BUILD_ROOT/lib/modules/%{KernelVer}/System.map # DTBs — install ALL DTBs the kernel built, then prune to keep only the # Pocket DS one. The rocknix .config has CONFIG_ARCH_QCOM=y which makes # Kbuild compile every Qualcomm DTB (~700 of them, ~30MB) — we only need # qcs8550-ayaneo-pocketds.dtb on the FAT ROCKNIX partition. Mirror the # trimmed tree under /lib/modules//dtb/ for any consumer that # walks the modules dir. mkdir -p $RPM_BUILD_ROOT/boot/dtb-%{KernelVer} make ARCH=%{asmarch} INSTALL_DTBS_PATH=$RPM_BUILD_ROOT/boot/dtb-%{KernelVer} dtbs_install find $RPM_BUILD_ROOT/boot/dtb-%{KernelVer} -type f \ ! -name 'qcs8550-ayaneo-pocketds.dtb' -delete find $RPM_BUILD_ROOT/boot/dtb-%{KernelVer} -type d -empty -delete mkdir -p $RPM_BUILD_ROOT/boot/dtb-%{KernelVer}/qcom cp -r $RPM_BUILD_ROOT/boot/dtb-%{KernelVer} $RPM_BUILD_ROOT/lib/modules/%{KernelVer}/dtb # Kernel modules make %{?_smp_mflags} ARCH=%{asmarch} \ INSTALL_MOD_PATH=$RPM_BUILD_ROOT \ KERNELRELEASE=%{KernelVer} \ modules_install mod-fw= # Drop module symlinks pointing at the build root (rebuilt by kernel-devel) rm -f $RPM_BUILD_ROOT/lib/modules/%{KernelVer}/build rm -f $RPM_BUILD_ROOT/lib/modules/%{KernelVer}/source # kernel-devel: headers + Makefile / Kconfig / scripts to build out-of-tree mkdir -p $RPM_BUILD_ROOT/usr/src/kernels/%{KernelVer} DEVEL=$RPM_BUILD_ROOT/usr/src/kernels/%{KernelVer} cp --parents $(find -type f \( -name "Makefile*" -o -name "Kconfig*" \)) $DEVEL/ cp Module.symvers $DEVEL/ cp System.map $DEVEL/ cp .config $DEVEL/ cp -a include $DEVEL/ cp -a scripts $DEVEL/ mkdir -p $DEVEL/arch/arm64 cp -a arch/arm64/include $DEVEL/arch/arm64/ cp -a arch/arm64/Makefile $DEVEL/arch/arm64/ cp -a arch/arm64/Kconfig $DEVEL/arch/arm64/ 2>/dev/null || : # Symlink build/source -> kernel-devel tree ln -s /usr/src/kernels/%{KernelVer} $RPM_BUILD_ROOT/lib/modules/%{KernelVer}/build ln -s /usr/src/kernels/%{KernelVer} $RPM_BUILD_ROOT/lib/modules/%{KernelVer}/source # Run depmod against the staged tree /sbin/depmod -aeF $RPM_BUILD_ROOT/boot/System.map-%{KernelVer} \ -b $RPM_BUILD_ROOT %{KernelVer} # No %post / %postun. The vanilla Fedora kernel.spec calls # `kernel-install add` to copy kernel + initrd into /boot// # and write a BLS entry under /boot/loader/entries/. We don't run an # initramfs and the ROCKNIX-signed ABL doesn't read BLS — those files # would just bloat the FAT and shadow our /boot/Image. # # We DO need a %posttrans to maintain the unversioned /boot/Image # symlink that the ABL actually loads. Per-kernel files are at # /boot/Image- (rpm-tracked); the unversioned alias is a # plain symlink (rpm-untracked, so two installed kernels don't fight # over /boot/Image like they did before — that conflict is what # triggered "file /boot/Image from install of kernel-X.Y conflicts # with file from package kernel-X.Z" on dnf upgrade). %posttrans # After every install/upgrade transaction: refresh both # /boot/Image (FAT root -- one of the paths the ABL probes) # /boot/boot/Image (FAT \boot\Image -- the canonical ABL load # path, originally populated only by # mkosi.repart at image build time) # # cp not ln because the device's fstab mounts /boot from the FAT # ROCKNIX partition and vfat doesn't support symlinks. `ls -v` sorts # numerically so tail -n1 is the newest NVR. The cp is ~16 MB -- # negligible on every kernel upgrade. # # Without writing /boot/boot/Image specifically, on-device dnf # upgrade kernel-X never refreshes the path the ABL actually loads, # and the device keeps booting whatever was flashed at image build # time -- verified live on a Pocket DS that was stuck on kernel-100.* # after multiple kernel-101.* upgrades. latest=$(ls -v /boot/Image-*.aarch64 2>/dev/null | tail -n1) if [ -n "$latest" ]; then /usr/bin/cp -f "$latest" /boot/Image if [ -d /boot/boot ]; then /usr/bin/cp -f "$latest" /boot/boot/Image fi fi exit 0 %postun # Same logic on uninstall: re-point both Image paths at whatever # Image- is still on disk (which after `dnf remove kernel-OLD` # is the still-installed newer kernel). If nothing is left, leave # the existing Images alone -- removing them would leave the device # unbootable, and the user is presumably about to flash a fresh image. latest=$(ls -v /boot/Image-*.aarch64 2>/dev/null | tail -n1) if [ -n "$latest" ]; then /usr/bin/cp -f "$latest" /boot/Image if [ -d /boot/boot ]; then /usr/bin/cp -f "$latest" /boot/boot/Image fi fi exit 0 # --------------------------------------------------------------- files ---- %files %defattr(-,root,root) /boot/Image-%{KernelVer} /boot/config-%{KernelVer} /boot/System.map-%{KernelVer} /boot/dtb-%{KernelVer} /lib/modules/%{KernelVer} %files devel %defattr(-,root,root) /usr/src/kernels/%{KernelVer} %changelog * Wed May 06 2026 Pocket DS Maintainers - %{version}-%{release} - Release auto-stamped from build_timestamp + .pocketds suffix; full history in git log