|
| 1 | +# Credits: https://github.com/tokio-rs/io-uring/blob/master/.github/workflows/kernel-version-test.yml |
| 2 | +# |
| 3 | +# Tests kTLS functionality across multiple kernel versions. |
| 4 | +# Manual trigger supports custom space-separated version list. |
| 5 | + |
| 6 | +name: Kernel Compatibility Test |
| 7 | + |
| 8 | +on: |
| 9 | + push: |
| 10 | + branches: ["main"] |
| 11 | + pull_request: |
| 12 | + merge_group: |
| 13 | + schedule: |
| 14 | + # Every day at 04:00 UTC |
| 15 | + - cron: "0 4 * * *" |
| 16 | + workflow_dispatch: |
| 17 | + inputs: |
| 18 | + kernel-versions: |
| 19 | + description: "Space-separated list of Linux kernel versions to test" |
| 20 | + required: true |
| 21 | + |
| 22 | +permissions: |
| 23 | + contents: read |
| 24 | + |
| 25 | +jobs: |
| 26 | + prepare-kernel-versions: |
| 27 | + name: Prepare kernel versions matrix |
| 28 | + runs-on: ubuntu-latest |
| 29 | + outputs: |
| 30 | + matrix: ${{ steps.set-matrix.outputs.matrix }} |
| 31 | + steps: |
| 32 | + - name: Install dependencies |
| 33 | + run: | |
| 34 | + sudo apt-get update |
| 35 | + sudo apt-get install -y jq curl |
| 36 | +
|
| 37 | + - name: Set matrix |
| 38 | + id: set-matrix |
| 39 | + run: | |
| 40 | + if [ -n "${GITHUB_EVENT_INPUTS_KERNEL_VERSIONS}" ]; then |
| 41 | + # Manual trigger with custom versions |
| 42 | + versions="${GITHUB_EVENT_INPUTS_KERNEL_VERSIONS}" |
| 43 | + echo "Using manual input versions: $versions" |
| 44 | + else |
| 45 | + # Get from https://www.kernel.org/releases.json |
| 46 | + echo "Fetching latest kernel versions from kernel.org..." |
| 47 | + |
| 48 | + # Fetch releases data |
| 49 | + releases_json=$(curl -s https://www.kernel.org/releases.json) |
| 50 | +
|
| 51 | + if [ -z "$releases_json" ]; then |
| 52 | + echo "Failed to fetch kernel releases data" |
| 53 | + exit 1 |
| 54 | + fi |
| 55 | + |
| 56 | + # Extract versions for specific kernel series |
| 57 | + mainline=$(echo "$releases_json" | jq -r '.releases[] | select(.moniker == "mainline") | .version') |
| 58 | + stable=$(echo "$releases_json" | jq -r '.releases[] | select(.moniker == "stable") | .version') |
| 59 | + |
| 60 | + # Extract latest longterm versions for specific series |
| 61 | + lts_6_12=$(echo "$releases_json" | jq -r '.releases[] | select(.moniker == "longterm" and (.version | startswith("6.12."))) | .version' | head -1) |
| 62 | + lts_6_6=$(echo "$releases_json" | jq -r '.releases[] | select(.moniker == "longterm" and (.version | startswith("6.6."))) | .version' | head -1) |
| 63 | + lts_6_1=$(echo "$releases_json" | jq -r '.releases[] | select(.moniker == "longterm" and (.version | startswith("6.1."))) | .version' | head -1) |
| 64 | + lts_5_15=$(echo "$releases_json" | jq -r '.releases[] | select(.moniker == "longterm" and (.version | startswith("5.15."))) | .version' | head -1) |
| 65 | + lts_5_10=$(echo "$releases_json" | jq -r '.releases[] | select(.moniker == "longterm" and (.version | startswith("5.10."))) | .version' | head -1) |
| 66 | + lts_5_4=$(echo "$releases_json" | jq -r '.releases[] | select(.moniker == "longterm" and (.version | startswith("5.4."))) | .version' | head -1) |
| 67 | + |
| 68 | + # Build versions list with fallbacks for missing LTS versions |
| 69 | + versions="" |
| 70 | + [ -n "$mainline" ] && versions="$versions $mainline" |
| 71 | + [ -n "$stable" ] && versions="$versions $stable" |
| 72 | + [ -n "$lts_6_12" ] && versions="$versions $lts_6_12" |
| 73 | + [ -n "$lts_6_6" ] && versions="$versions $lts_6_6" |
| 74 | + [ -n "$lts_6_1" ] && versions="$versions $lts_6_1" |
| 75 | + [ -n "$lts_5_15" ] && versions="$versions $lts_5_15" |
| 76 | + [ -n "$lts_5_10" ] && versions="$versions $lts_5_10" |
| 77 | + [ -n "$lts_5_4" ] && versions="$versions $lts_5_4" |
| 78 | + fi |
| 79 | +
|
| 80 | + # Convert space-separated list to JSON array |
| 81 | + json_array=$(echo "$versions" | xargs | tr ' ' '\n' | grep -v '^$' | jq -R . | jq -s -c .) |
| 82 | + echo "matrix=$json_array" >> $GITHUB_OUTPUT |
| 83 | + echo "Generated matrix: $json_array" |
| 84 | + env: |
| 85 | + GITHUB_EVENT_INPUTS_KERNEL_VERSIONS: ${{ github.event.inputs.kernel-versions }} |
| 86 | + |
| 87 | + build-test-binary: |
| 88 | + name: Build test binary |
| 89 | + runs-on: ubuntu-latest |
| 90 | + steps: |
| 91 | + - name: Checkout repository |
| 92 | + uses: actions/checkout@v4 |
| 93 | + with: |
| 94 | + persist-credentials: false |
| 95 | + |
| 96 | + - name: Install dependencies |
| 97 | + run: | |
| 98 | + sudo apt-get update |
| 99 | + sudo apt-get install -y musl-tools |
| 100 | +
|
| 101 | + - name: Install Rust 1.83.0 |
| 102 | + uses: dtolnay/rust-toolchain@master |
| 103 | + with: |
| 104 | + toolchain: 1.83.0 |
| 105 | + targets: x86_64-unknown-linux-musl |
| 106 | + |
| 107 | + - name: Build test binary |
| 108 | + run: | |
| 109 | + cargo build --package ktls-tests --bin ktls-test-echo --release --target x86_64-unknown-linux-musl |
| 110 | +
|
| 111 | + - name: Upload artifact |
| 112 | + uses: actions/upload-artifact@v4 |
| 113 | + with: |
| 114 | + name: ktls-test-echo |
| 115 | + path: target/x86_64-unknown-linux-musl/release/ktls-test-echo |
| 116 | + retention-days: 1 |
| 117 | + compression-level: 0 |
| 118 | + overwrite: true |
| 119 | + |
| 120 | + build-kernel: |
| 121 | + name: Build kernel - ${{ matrix.kernel-version }} |
| 122 | + needs: prepare-kernel-versions |
| 123 | + runs-on: ubuntu-latest |
| 124 | + strategy: |
| 125 | + matrix: |
| 126 | + kernel-version: ${{fromJson(needs.prepare-kernel-versions.outputs.matrix)}} |
| 127 | + fail-fast: false |
| 128 | + env: |
| 129 | + KERNEL_VERSION: ${{ matrix.kernel-version }} |
| 130 | + steps: |
| 131 | + - name: Install dependencies |
| 132 | + run: | |
| 133 | + sudo apt-get update |
| 134 | + sudo apt-get install -y \ |
| 135 | + bison flex libelf-dev \ |
| 136 | + xz-utils wget |
| 137 | +
|
| 138 | + - name: Cache Linux source |
| 139 | + id: cache-kernel |
| 140 | + uses: actions/cache@v4 |
| 141 | + with: |
| 142 | + path: linux-${{ env.KERNEL_VERSION }} |
| 143 | + key: linux-${{ env.KERNEL_VERSION }}-cache-v1 |
| 144 | + |
| 145 | + - name: Build Linux kernel |
| 146 | + if: steps.cache-kernel.outputs.cache-hit != 'true' |
| 147 | + run: | |
| 148 | + MAJOR=${KERNEL_VERSION%%.*} |
| 149 | + wget https://cdn.kernel.org/pub/linux/kernel/v${MAJOR}.x/linux-${KERNEL_VERSION}.tar.xz |
| 150 | + tar xf linux-${KERNEL_VERSION}.tar.xz |
| 151 | + cd linux-${KERNEL_VERSION} |
| 152 | +
|
| 153 | + # Generate the default config |
| 154 | + make defconfig |
| 155 | +
|
| 156 | + # Enable essentials as built-ins |
| 157 | + scripts/config --enable CONFIG_DEVTMPFS |
| 158 | + scripts/config --enable CONFIG_DEVTMPFS_MOUNT |
| 159 | +
|
| 160 | + # Enable virtio drivers |
| 161 | + scripts/config --enable CONFIG_VIRTIO |
| 162 | + scripts/config --enable CONFIG_VIRTIO_PCI |
| 163 | + scripts/config --enable CONFIG_VIRTIO_BLK |
| 164 | +
|
| 165 | + # Enable kTLS support |
| 166 | + scripts/config --enable CONFIG_TLS |
| 167 | + scripts/config --enable CONFIG_TLS_DEVICE |
| 168 | +
|
| 169 | + # Generate the updated config |
| 170 | + make olddefconfig |
| 171 | +
|
| 172 | + make -j$(nproc) |
| 173 | +
|
| 174 | + - name: Upload kernel artifact |
| 175 | + uses: actions/upload-artifact@v4 |
| 176 | + with: |
| 177 | + name: linux-kernel-${{ env.KERNEL_VERSION }} |
| 178 | + path: linux-${{ env.KERNEL_VERSION }}/arch/x86/boot/bzImage |
| 179 | + retention-days: 1 |
| 180 | + compression-level: 6 |
| 181 | + overwrite: true |
| 182 | + |
| 183 | + test: |
| 184 | + name: Test - ${{ matrix.kernel-version }} @ ${{ matrix.arg-termination }} ${{ matrix.arg-cipher }} |
| 185 | + needs: [prepare-kernel-versions, build-test-binary, build-kernel] |
| 186 | + runs-on: ubuntu-latest |
| 187 | + strategy: |
| 188 | + matrix: |
| 189 | + kernel-version: ${{fromJson(needs.prepare-kernel-versions.outputs.matrix)}} |
| 190 | + arg-termination: ["client", "server"] |
| 191 | + arg-cipher: |
| 192 | + [ |
| 193 | + "TLS13_AES_128_GCM_SHA256", |
| 194 | + "TLS13_AES_256_GCM_SHA384", |
| 195 | + "TLS13_CHACHA20_POLY1305_SHA256", |
| 196 | + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", |
| 197 | + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", |
| 198 | + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", |
| 199 | + ] |
| 200 | + fail-fast: false |
| 201 | + env: |
| 202 | + KERNEL_VERSION: ${{ matrix.kernel-version }} |
| 203 | + TEST_ARG_TERMINATION: ${{ matrix.arg-termination }} |
| 204 | + TEST_ARG_CIPHER: ${{ matrix.arg-cipher }} |
| 205 | + steps: |
| 206 | + - name: Install dependencies |
| 207 | + run: | |
| 208 | + sudo apt-get update |
| 209 | + sudo apt-get install -y \ |
| 210 | + qemu-system-x86 busybox-static cpio e2fsprogs |
| 211 | +
|
| 212 | + - name: Download test binary artifact |
| 213 | + uses: actions/download-artifact@v4 |
| 214 | + with: |
| 215 | + name: ktls-test-echo |
| 216 | + path: . |
| 217 | + |
| 218 | + - name: Download kernel artifact |
| 219 | + uses: actions/download-artifact@v4 |
| 220 | + with: |
| 221 | + name: linux-kernel-${{ env.KERNEL_VERSION }} |
| 222 | + path: . |
| 223 | + |
| 224 | + - name: Prepare initramfs + tests binaries |
| 225 | + run: | |
| 226 | + rm -rf initramfs && mkdir -p initramfs/{bin,sbin,proc,sys,tmp} |
| 227 | +
|
| 228 | + # Copy the test binary (downloaded from artifact) |
| 229 | + chmod +x ktls-test-echo |
| 230 | + cp ktls-test-echo initramfs/bin/ |
| 231 | +
|
| 232 | + # Add necessary binaries from busybox |
| 233 | + cp /usr/bin/busybox initramfs/bin/ |
| 234 | + for cmd in sh mount ip ifconfig cat; do ln -sf busybox initramfs/bin/$cmd; done |
| 235 | + ln -sf ../bin/busybox initramfs/sbin/poweroff |
| 236 | +
|
| 237 | + # Generate init script |
| 238 | + cat > initramfs/init << EOF |
| 239 | + #!/bin/sh |
| 240 | + set -e |
| 241 | +
|
| 242 | + # Activating the loopback interface (it's required for some network tests) |
| 243 | + ip link set lo up |
| 244 | +
|
| 245 | + mkdir -p /dev |
| 246 | +
|
| 247 | + # Enable necessary devices |
| 248 | + # https://www.kernel.org/doc/Documentation/admin-guide/devices.txt |
| 249 | + mknod /dev/port c 1 4 |
| 250 | + mknod /dev/null c 1 3 |
| 251 | + mknod /dev/zero c 1 5 |
| 252 | + mknod /dev/tty c 5 0 |
| 253 | +
|
| 254 | + mkdir -p /tmp && mount -t tmpfs -o mode=1777 tmpfs /tmp |
| 255 | +
|
| 256 | + # Bring up ext4 test volume at /mnt |
| 257 | + mount -t devtmpfs devtmpfs /dev |
| 258 | +
|
| 259 | + exit_code=0 |
| 260 | +
|
| 261 | + # Run the test binary |
| 262 | + RUST_BACKTRACE=1 /bin/ktls-test-echo -t "${TEST_ARG_TERMINATION}" -c "${TEST_ARG_CIPHER}" || exit_code=1 |
| 263 | +
|
| 264 | + # If the test binary exited with a non-zero code, write it to /dev/port. |
| 265 | + # This lets QEMU exit with non-zero exit-code, triggering a CI error. |
| 266 | + [ \$exit_code -eq 0 ] || printf '\x01' \\ |
| 267 | + | dd of=/dev/port bs=1 seek=244 count=1 2>/dev/null |
| 268 | +
|
| 269 | + /sbin/poweroff -f |
| 270 | +
|
| 271 | + EOF |
| 272 | +
|
| 273 | + chmod +x initramfs/init |
| 274 | +
|
| 275 | + # Pack into a CPIO archive |
| 276 | + (cd initramfs && find . -print0 \ |
| 277 | + | cpio --null -ov --format=newc | gzip -9 > ../initramfs.cpio.gz) |
| 278 | +
|
| 279 | + - name: Run tests in QEMU |
| 280 | + run: | |
| 281 | + qemu-system-x86_64 \ |
| 282 | + -device isa-debug-exit,iobase=0xf4,iosize=0x04 \ |
| 283 | + -kernel bzImage \ |
| 284 | + -initrd initramfs.cpio.gz \ |
| 285 | + -netdev user,id=net0 \ |
| 286 | + -device e1000,netdev=net0 \ |
| 287 | + -append "console=ttyS0 rootfstype=ramfs panic=1" \ |
| 288 | + -nographic -no-reboot -m 1024 -action panic=exit-failure |
| 289 | +
|
| 290 | + if [ $? -ne 0 ]; then |
| 291 | + echo "tests failed (QEMU exited abnormally)" |
| 292 | + exit 1 |
| 293 | + else |
| 294 | + echo "all tests passed" |
| 295 | + fi |
0 commit comments