Repository: electron/electron
Pull request: ci: test linux 64k

#### Description of Change
This PR adds a sanity test for 64K page kernels on Linux/AArch64 (arm64).  Chromium enabled this in https://chromium-review.googlesource.com/c/chromium/src/+/5201191.  

VS Code recently ran into an issue where Electron was not launching on  64K page kernel: https://github.com/microsoft/vscode/issues/268354

qemu is used to emulate a 64k page kernel which means a slower testing platform, so the test runs a simple electron app to verify that Electron launches.  In the future if we can create a GitHub Actions runner running on a 64k page kernel, in theory we could run our full test suite.
<!--
Thank you for your Pull Request. Please provide a description above and review
the requirements below.

Contributors guide: https://github.com/electron/electron/blob/main/CONTRIBUTING.md
-->

#### Checklist
<!-- Remove items that do not apply. For completed items, change [ ] to [x]. -->

- [x] PR description included
- [x] I have built and tested this PR
- [x] `npm test` passes
- [x] [PR release notes](https://github.com/electron/clerk/blob/main/README.md) describe the change in a way relevant to app developers, and are [capitalized, punctuated, and past tense](https://github.com/electron/clerk/blob/main/README.md#examples).

#### Release Notes

Notes: <!-- Please add a one-line description for app developers to read in the release notes, or 'none' if no notes relevant to app developers. Examples and help on special cases: https://github.com/electron/clerk/blob/main/README.md#examples -->none


Changed files:
filename: .github/workflows/build.yml
patch: @@ -365,6 +365,18 @@ jobs:
       generate-symbols: false
       upload-to-storage: '0'
     secrets: inherit
+    
+  test-linux-arm64-64k:
+    uses: ./.github/workflows/pipeline-segment-electron-test-64k.yml
+    permissions:
+      contents: read
+      issues: read
+      pull-requests: read
+    needs: [checkout-linux, linux-arm64]
+    with:
+      test-runs-on: ubuntu-22.04-arm
+      test-container: '{"image":"ghcr.io/electron/test:arm64v8-${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root --privileged --init"}'
+    secrets: inherit
 
   windows-x64:
     permissions:

filename: .github/workflows/pipeline-segment-electron-test-64k.yml
patch: @@ -0,0 +1,67 @@
+name: Pipeline Segment - Electron Test on Linux ARM64 64k
+
+on:
+  workflow_call:
+    inputs:
+      test-runs-on:
+        type: string
+        description: 'What host to run the tests on'
+        required: true
+      test-container:
+        type: string
+        description: 'JSON container information for aks runs-on'
+        required: false
+        default: '{"image":null}'
+
+concurrency:
+  group: electron-test-linux-64k-${{ github.ref_protected == true && github.run_id || github.ref }}
+  cancel-in-progress: ${{ github.ref_protected != true }}
+
+permissions: {}
+
+env:
+  ELECTRON_OUT_DIR: Default
+
+jobs:
+  test-linux-arm64-64k:
+    env:
+      BUILD_TYPE: linux
+      TARGET_ARCH: arm64      
+    defaults:
+      run:
+        shell: bash
+    runs-on: ${{ inputs.test-runs-on }}
+    permissions:
+      contents: read
+      issues: read
+      pull-requests: read
+    steps:
+    - name: Checkout Electron
+      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
+      with:
+        path: src/electron
+        fetch-depth: 0
+        ref: ${{ github.event.pull_request.head.sha }}
+    - name: Download Generated Artifacts
+      uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131
+      with:
+        name: generated_artifacts_linux_arm64
+        path: ./generated_artifacts_linux_arm64
+    - name: Restore Generated Artifacts
+      run: ./src/electron/script/actions/restore-artifacts.sh
+    - name: Unzip Dist
+      run: |
+        cd src/out/Default
+        unzip -:o dist.zip
+
+    - name: Run Electron Tests in QEMU 64k Container
+      shell: bash
+      env:
+        MOCHA_REPORTER: mocha-multi-reporters
+        MOCHA_MULTI_REPORTERS: mocha-junit-reporter, tap
+        ELECTRON_DISABLE_SECURITY_WARNINGS: 1
+        DISPLAY: ':99.0'
+      run: |
+        container=$(echo '${{ inputs.test-container }}' | jq -r '.image')
+        src/electron/script/run-qemu-64k.sh --container $container --testfiles "`pwd`/src"
+        
\ No newline at end of file

filename: script/qemu-init.sh
patch: @@ -0,0 +1,44 @@
+#!/bin/sh
+
+echo "Mounting essential filesystems"
+mount -t proc proc /proc
+mount -t sysfs sys /sys
+mkdir -p /dev/pts
+mount -t devpts devpts /dev/pts
+mkdir -p /dev/shm
+mount -t tmpfs tmpfs /dev/shm
+mount -t tmpfs tmpfs /tmp
+chmod 1777 /tmp
+mount -t tmpfs tmpfs /run
+mkdir -p /run/dbus
+mkdir -p /run/user/0
+chmod 700 /run/user/0
+mount -t tmpfs tmpfs /var/tmp
+
+echo "Setting up hostname and machine-id for D-Bus"
+echo "electron-test" > /etc/hostname
+hostname electron-test
+cat /proc/sys/kernel/random/uuid | tr -d '-' > /etc/machine-id
+
+echo "Setting system clock"
+date -s "$(cat /host-time)"
+
+
+export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+export XDG_RUNTIME_DIR=/run/user/0
+
+echo "Starting entrypoint"
+echo "System: $(uname -s) $(uname -r) $(uname -m), page size: $(getconf PAGESIZE) bytes"
+sudo chown -R builduser:builduser /home/builduser
+ls -la /home/builduser/src/out/Default/electron
+cd /home/builduser/src
+runuser -u builduser -- xvfb-run out/Default/electron electron/spec/fixtures/apps/open-new-window-from-link
+EXIT_CODE=$?
+echo "Test execution finished with exit code $EXIT_CODE"
+echo $EXIT_CODE > /exit-code
+sync
+
+echo "Powering off"
+# poweroff -f bypasses the init system (this script IS pid 1) and
+# directly invokes the reboot() syscall, causing QEMU to exit immediately.
+poweroff -f

filename: script/run-qemu-64k.sh
patch: @@ -0,0 +1,100 @@
+#!/bin/sh
+set -e
+
+CONTAINER=""
+TESTFILES=""
+ARGS=""
+
+while [ $# -gt 0 ]; do
+	case "$1" in
+		--container) CONTAINER="$2"; shift 2 ;;
+		--testfiles) TESTFILES="$2"; shift 2 ;;
+		--) shift; ARGS="$*"; break ;;
+		*) echo "Unknown option: $1"; exit 1 ;;
+	esac
+done
+
+if [ -z "$CONTAINER" ]; then
+	echo "Usage: $0 --container CONTAINER [-- ARGS...]"
+	exit 1
+fi
+
+echo "Installing QEMU system emulation and tools"
+sudo apt-get update && sudo apt-get install -y qemu-system-arm binutils
+
+KERNEL_URL="http://ports.ubuntu.com/ubuntu-ports/pool/main/l/linux/linux-image-unsigned-6.8.0-90-generic-64k_6.8.0-90.91_arm64.deb"
+KERNEL_DIR=$(mktemp -d)
+ROOTFS_DIR=$(mktemp -d)
+
+# Download kernel and export container filesystem in parallel
+echo "Downloading kernel and exporting container filesystem in parallel"
+curl -fL "$KERNEL_URL" -o "$KERNEL_DIR/kernel.deb" &
+CURL_PID=$!
+
+CONTAINER_ID=$(docker create --platform linux/arm64 "$CONTAINER")
+docker export "$CONTAINER_ID" | sudo tar -xf - -C "$ROOTFS_DIR"
+docker rm -f "$CONTAINER_ID"
+
+echo "Removing container image to free disk space"
+docker rmi "$CONTAINER" || true
+docker system prune -f || true
+
+wait $CURL_PID
+
+echo "Extracting kernel"
+(cd "$KERNEL_DIR" && ar x kernel.deb && tar xf data.tar*)
+VMLINUZ="$KERNEL_DIR/boot/vmlinuz-6.8.0-90-generic-64k"
+if [ ! -f "$VMLINUZ" ]; then
+	echo "Error: Could not find kernel at $VMLINUZ"
+	exit 1
+fi
+
+sudo cp -r $TESTFILES "$ROOTFS_DIR/home/builduser"
+
+echo "Storing test arguments and installing init script"
+echo "$ARGS" > "$ROOTFS_DIR/test-args"
+date -u '+%Y-%m-%d %H:%M:%S' > "$ROOTFS_DIR/host-time"
+sudo cp "$TESTFILES/electron/script/qemu-init.sh" "$ROOTFS_DIR/init"
+sudo chmod +x "$ROOTFS_DIR/init"
+
+echo "Creating disk image with root filesystem"
+df -h
+DISK_IMG=$(mktemp)
+truncate -s 10G "$DISK_IMG"
+sudo mkfs.ext4 -q -d "$ROOTFS_DIR" "$DISK_IMG"
+sudo rm -rf "$ROOTFS_DIR"
+
+# Use KVM acceleration if available (ARM64 host can run 64K-page guest via KVM)
+if [ -e /dev/kvm ] && [ -w /dev/kvm ]; then
+	echo "KVM available, using hardware acceleration"
+	ACCEL="-accel kvm -cpu host"
+else
+	echo "KVM not available, using TCG emulation"
+	ACCEL="-accel tcg,thread=multi -cpu max,pauth-impdef=on"
+fi
+
+echo "Starting QEMU VM with 64K page size kernel"
+timeout 1800 qemu-system-aarch64 \
+	-M virt \
+	$ACCEL \
+	-m 4096 \
+	-smp 2 \
+	-kernel "$VMLINUZ" \
+	-append "console=ttyAMA0 root=/dev/vda rw init=/init net.ifnames=0 panic=1" \
+	-drive file="$DISK_IMG",format=raw,if=virtio \
+	-virtfs local,path="$TESTFILES",mount_tag=testfiles,security_model=none,id=testfiles \
+	-netdev user,id=net0 \
+	-device virtio-net-pci,netdev=net0 \
+	-nographic \
+	-no-reboot \
+	|| true
+
+echo "Extracting test results from disk image"
+MOUNT_DIR=$(mktemp -d)
+sudo mount -o loop "$DISK_IMG" "$MOUNT_DIR"
+if [ -f "$MOUNT_DIR/results.xml" ]; then
+	cp "$MOUNT_DIR/results.xml" .
+fi
+EXIT_CODE=$(cat "$MOUNT_DIR/exit-code" 2>/dev/null || echo 1)
+sudo umount "$MOUNT_DIR"
+exit $EXIT_CODE