Author SHA1 Message Date
Bug Bounty Zip 90f06ee77b Add Kali internal IW620 WiFi support 2026-05-29 15:53:36 +03:00
Bug Bounty Zip b68aa667d1 Extend image build timeout 2026-05-27 15:22:44 +03:00
Bug Bounty Zip a1e2bc92e5 Reduce Kali release image scope 2026-05-27 15:19:17 +03:00
Bug Bounty Zip 0adaf1c6d6 Use Kali snapshot for release builds 2026-05-27 10:38:58 +03:00
Bug Bounty Zip d0efec98fe Harden self-hosted image builds 2026-05-27 09:17:16 +03:00
Bug Bounty ZipandGitHub 7f905df12c Merge pull request #11 from ps5-linux/kali-official-support
Include Kali in full image builds
2026-05-27 01:37:57 +03:00
Bug Bounty Zip daf634a6ed Include Kali in full image builds 2026-05-27 01:36:48 +03:00
Bug Bounty ZipandGitHub 703dc736e9 Merge pull request #10 from ps5-linux/fix/arch-cachyos-mirrors
Fix Arch mirror selection in image builds
2026-05-27 01:15:15 +03:00
Bug Bounty Zip dc365dda15 Fix Arch mirror selection in image builds 2026-05-26 21:18:14 +03:00
Bug Bounty ZipandGitHub 8160bc649b Add Kali Linux image support (#9)
* Add Kali Linux image support

* Remove forced 1080p from Kali cmdline
2026-05-25 21:16:56 +02:00
Andy Nguyen 1ea662d598 Update to v1.2 patches. 2026-05-25 07:44:00 +02:00
Andy Nguyen 04378b78f4 Make backup in zz-update-boot. 2026-05-24 23:01:23 +02:00
DanandGitHub 0e1219f422 deprecate alpine (#7) 2026-05-19 09:11:54 +02:00
Dan Kluser 33189d7e7b ci: pull from branch for ci 2026-05-19 07:43:52 +02:00
DanandGitHub d92e51959b Merge pull request #5 from Shivox/fix-firstboot-cachyos
fix first-boot flow for CachyOS
2026-05-14 18:55:39 +02:00
Dan Kluser 01205507fd CI: no releases for pr runs 2026-05-14 13:15:15 +02:00
Dan Kluser 9d65d7a391 manual trigger for PRs 2026-05-14 13:07:52 +02:00
Dan Kluser d55d87e6bd move actions to image repo 2026-05-14 12:45:22 +02:00
Chihab Djaidja 4a2aef2e03 Fix missing so error 2026-05-12 15:38:08 +02:00
Chihab Djaidja 2d07c87a78 fix first-boot flow 2026-05-12 14:14:11 +02:00
Dan Kluser 4cf616a934 allow manual triggers 2026-05-11 20:49:45 +02:00
Dan Kluser 142d3913c9 remove systemctl enable for qemu compat 2026-05-10 21:17:01 +02:00
Andy Nguyen a76971ba96 Update to v1.1 patches. 2026-05-10 14:20:26 +02:00
Dan Kluser 3db57b8de7 fix cachy with new kernel pkg 2026-05-10 09:15:26 +02:00
Dan Kluser 73bfdd288b fix for arch under qemu 2026-05-10 08:57:43 +02:00
Dan Kluser fda932a3a3 fix arch under qemu build and cachy kernel 2026-05-10 07:15:52 +02:00
Dan Kluser 43d2a65997 add CI trigger workflow 2026-05-09 23:06:04 +02:00
DanandGitHub 492e124991 Merge pull request #4 from ps5-linux/feature/ci-integration
Feature/ci integration
2026-05-09 18:14:38 +02:00
24 changed files with 1154 additions and 462 deletions
-1
View File
@@ -1,7 +1,6 @@
linux_deb/
linux-bin/
work/
work-alpine/
output/
.git/
ps5-linux-patches/
+13
View File
@@ -0,0 +1,13 @@
* text=auto
*.sh text eol=lf
*.bash text eol=lf
*.yaml text eol=lf
*.yml text eol=lf
*.service text eol=lf
build_image.sh text eol=lf
boot/kexec*.sh text eol=lf
distros/**/grow-rootfs text eol=lf
distros/shared/zz-update-boot* text eol=lf
docker/** text eol=lf
+206
View File
@@ -0,0 +1,206 @@
name: Build Image
run-name: Build Image (${{ inputs.distro || 'all' }})
on:
workflow_dispatch:
inputs:
distro:
description: 'Distribution'
default: 'ubuntu2604'
type: choice
options: [all, ubuntu2604, arch, cachyos, kali]
workflow_call:
inputs:
distro:
type: string
default: 'ubuntu2604'
secrets:
REMOTE_SSH_KEY:
required: true
REMOTE_SSH_CONFIG:
required: true
REMOTE_SSH_KNOWN_HOSTS:
required: true
R2_ACCESS_KEY_ID:
required: true
R2_SECRET_ACCESS_KEY:
required: true
R2_ENDPOINT:
required: true
R2_BUCKET:
required: true
jobs:
matrix:
runs-on: self-hosted
outputs:
distros: ${{ steps.set.outputs.distros }}
steps:
- id: set
run: |
INPUT="${{ inputs.distro || 'ubuntu2604' }}"
if [ "$INPUT" = "all" ]; then
echo 'distros=["kali","ubuntu2604","arch","cachyos"]' >> "$GITHUB_OUTPUT"
else
echo "distros=[\"$INPUT\"]" >> "$GITHUB_OUTPUT"
fi
build:
needs: matrix
runs-on: self-hosted
timeout-minutes: 720
permissions:
contents: write
strategy:
fail-fast: true
max-parallel: 1
matrix:
distro: ${{ fromJson(needs.matrix.outputs.distros) }}
env:
CCACHE_DIR: /home/opc/ccache
steps:
- name: Clean image workspace
run: |
set -euo pipefail
if [ -z "${GITHUB_WORKSPACE:-}" ] || [ "$GITHUB_WORKSPACE" = "/" ]; then
echo "Invalid GITHUB_WORKSPACE" >&2
exit 1
fi
sudo rm -rf "$GITHUB_WORKSPACE/image"
- name: Checkout image builder
uses: actions/checkout@v6
with:
path: image
- name: Build ${{ matrix.distro }} image
run: |
bash image/build_image.sh --distro "${{ matrix.distro }}"
- name: Set version info
id: version
run: |
psha=$(git -C image/work/ps5-linux-patches rev-parse --short HEAD 2>/dev/null || echo "unknown")
kver=$(cat image/linux-bin/VERSION 2>/dev/null || echo "unknown")
ts=$(date -u +%Y%m%d-%H%M%S)
echo "psha=$psha" >> "$GITHUB_OUTPUT"
echo "kver=$kver" >> "$GITHUB_OUTPUT"
echo "ts=$ts" >> "$GITHUB_OUTPUT"
- name: Report
run: |
ls -lh image/output/*.img
sha256sum image/output/*.img
- name: Compress image
run: |
xz -T0 -9 -c image/output/ps5-${{ matrix.distro }}.img \
> image/output/ps5-${{ matrix.distro }}.img.xz
- name: Upload to R2
if: github.event_name != 'pull_request' && github.event_name != 'pull_request_target'
env:
RCLONE_CONFIG_R2_TYPE: s3
RCLONE_CONFIG_R2_PROVIDER: Cloudflare
RCLONE_CONFIG_R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
RCLONE_CONFIG_R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
RCLONE_CONFIG_R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
RCLONE_CONFIG_R2_NO_CHECK_BUCKET: true
run: |
rclone copyto image/output/ps5-${{ matrix.distro }}.img.xz \
"r2:${{ secrets.R2_BUCKET }}/ps5-${{ matrix.distro }}.img.xz"
- name: Save build metadata
run: |
mkdir -p meta
sha256sum image/output/ps5-${{ matrix.distro }}.img.xz \
| sed 's|image/output/||' > meta/${{ matrix.distro }}.sha256
echo "${{ steps.version.outputs.kver }}" > meta/kver
echo "${{ steps.version.outputs.psha }}" > meta/psha
echo "${{ steps.version.outputs.ts }}" > meta/ts
- name: Upload build metadata
uses: actions/upload-artifact@v6
with:
name: meta-${{ matrix.distro }}
path: meta/
retention-days: 1
- name: Upload to remote
if: github.event_name != 'pull_request' && github.event_name != 'pull_request_target'
env:
SSH_KEY: ${{ secrets.REMOTE_SSH_KEY }}
SSH_CONFIG: ${{ secrets.REMOTE_SSH_CONFIG }}
SSH_KNOWN_HOSTS: ${{ secrets.REMOTE_SSH_KNOWN_HOSTS }}
run: |
DIR=$(mktemp -d)
echo "$SSH_KEY" > "$DIR/key"
chmod 600 "$DIR/key"
echo "$SSH_CONFIG" > "$DIR/config"
sed -i "s|~/.ssh/remote_key|$DIR/key|" "$DIR/config"
echo "$SSH_KNOWN_HOSTS" > "$DIR/known_hosts"
SSH="ssh -F $DIR/config -o UserKnownHostsFile=$DIR/known_hosts"
SCP="scp -F $DIR/config -o UserKnownHostsFile=$DIR/known_hosts"
DEST="image-${{ matrix.distro }}-${{ steps.version.outputs.ts }}"
$SSH remote mkdir -p "./$DEST"
$SCP image/output/*.img.xz "remote:./$DEST/"
$SSH remote 'ls -dt image-${{ matrix.distro }}-*/ | tail -n +5 | xargs rm -rf' || true
rm -rf "$DIR"
release:
needs: build
runs-on: self-hosted
if: github.event_name != 'pull_request' && github.event_name != 'pull_request_target'
permissions:
contents: write
steps:
- name: Download build metadata
uses: actions/download-artifact@v6
with:
pattern: meta-*
path: meta
merge-multiple: true
- name: Prepare release body
id: body
run: |
KVER=$(cat meta/kver)
PSHA=$(cat meta/psha)
TS=$(cat meta/ts)
SUMS=$(cat meta/*.sha256)
{
echo 'text<<EOF'
echo "PS5 Linux images — built from latest \`main\`."
echo ""
echo "Kernel: \`$KVER\`"
echo "Patches: [\`$PSHA\`](https://github.com/ps5-linux/ps5-linux-patches/commit/$PSHA)"
echo "Built: \`$TS\`"
echo ""
echo "| Image | Download |"
echo "|-------|----------|"
echo "| Ubuntu 26.04 | [\`ps5-ubuntu2604.img.xz\`](https://pub-561df4012f1a46fbbdf618d5cc5941f6.r2.dev/ps5-ubuntu2604.img.xz) |"
echo "| Arch | [\`ps5-arch.img.xz\`](https://pub-561df4012f1a46fbbdf618d5cc5941f6.r2.dev/ps5-arch.img.xz) |"
echo "| CachyOS | [\`ps5-cachyos.img.xz\`](https://pub-561df4012f1a46fbbdf618d5cc5941f6.r2.dev/ps5-cachyos.img.xz) |"
if [ -f meta/kali.sha256 ]; then
echo "| Kali | [\`ps5-kali.img.xz\`](https://pub-561df4012f1a46fbbdf618d5cc5941f6.r2.dev/ps5-kali.img.xz) |"
fi
echo ""
echo "**SHA256 checksums:**"
echo "\`\`\`"
echo "$SUMS"
echo "\`\`\`"
echo 'EOF'
} >> "$GITHUB_OUTPUT"
- name: Create or update release
uses: softprops/action-gh-release@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
tag_name: latest
name: PS5 Linux Image (latest)
body: ${{ steps.body.outputs.text }}
make_latest: true
-18
View File
@@ -1,18 +0,0 @@
name: Dispatch to CI
on:
push:
branches:
- main
jobs:
dispatch:
runs-on: ubuntu-latest
steps:
- name: Send repository_dispatch
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.DISPATCH_TOKEN }}
repository: ps5-linux/ps5-linux-updates
event-type: image-builder-updated
client-payload: '{"sha": "${{ github.sha }}"}'
+172
View File
@@ -0,0 +1,172 @@
name: test uploads
on:
workflow_dispatch:
jobs:
matrix:
runs-on: self-hosted
outputs:
distros: ${{ steps.set.outputs.distros }}
steps:
- id: set
run: echo 'distros=["ubuntu2604","arch","cachyos"]' >> "$GITHUB_OUTPUT"
build:
needs: matrix
runs-on: self-hosted
timeout-minutes: 10
strategy:
fail-fast: false
max-parallel: 1
matrix:
distro: ${{ fromJson(needs.matrix.outputs.distros) }}
steps:
- name: Reset workspace
run: sudo rm -rf ./*
- name: Create fake image
run: |
mkdir -p image/output
dd if=/dev/urandom of=image/output/ps5-${{ matrix.distro }}.img bs=1M count=8
- name: Compress image
run: |
xz -T0 -1 -c image/output/ps5-${{ matrix.distro }}.img \
> image/output/ps5-${{ matrix.distro }}.img.xz
- name: Upload to R2
env:
RCLONE_CONFIG_R2_TYPE: s3
RCLONE_CONFIG_R2_PROVIDER: Cloudflare
RCLONE_CONFIG_R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
RCLONE_CONFIG_R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
RCLONE_CONFIG_R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
RCLONE_CONFIG_R2_NO_CHECK_BUCKET: true
run: |
rclone copyto image/output/ps5-${{ matrix.distro }}.img.xz \
"r2:${{ secrets.R2_BUCKET }}/test-${{ matrix.distro }}.img.xz" -v
- name: Save build metadata
run: |
mkdir -p meta
sha256sum image/output/ps5-${{ matrix.distro }}.img.xz \
| sed 's|image/output/||' > meta/${{ matrix.distro }}.sha256
echo "6.x.y-test" > meta/kver
echo "abc1234" > meta/psha
date -u +%Y%m%d-%H%M%S > meta/ts
- name: Upload build metadata
uses: actions/upload-artifact@v6
with:
name: meta-${{ matrix.distro }}
path: meta/
retention-days: 1
- name: Upload to remote
env:
SSH_KEY: ${{ secrets.REMOTE_SSH_KEY }}
SSH_CONFIG: ${{ secrets.REMOTE_SSH_CONFIG }}
SSH_KNOWN_HOSTS: ${{ secrets.REMOTE_SSH_KNOWN_HOSTS }}
run: |
DIR=$(mktemp -d)
echo "$SSH_KEY" > "$DIR/key"
chmod 600 "$DIR/key"
echo "$SSH_CONFIG" > "$DIR/config"
sed -i "s|~/.ssh/remote_key|$DIR/key|" "$DIR/config"
echo "$SSH_KNOWN_HOSTS" > "$DIR/known_hosts"
SSH="ssh -F $DIR/config -o UserKnownHostsFile=$DIR/known_hosts"
SCP="scp -F $DIR/config -o UserKnownHostsFile=$DIR/known_hosts"
$SSH remote mkdir -p ./test-upload
$SCP image/output/ps5-${{ matrix.distro }}.img.xz "remote:./test-upload/"
rm -rf "$DIR"
- name: Cleanup
if: always()
run: rm -rf image/output meta
release:
needs: build
runs-on: self-hosted
steps:
- name: Download build metadata
uses: actions/download-artifact@v6
with:
pattern: meta-*
path: meta
merge-multiple: true
- name: Prepare release body
id: body
run: |
KVER=$(cat meta/kver)
PSHA=$(cat meta/psha)
TS=$(cat meta/ts)
SUMS=$(cat meta/*.sha256)
{
echo 'text<<EOF'
echo "PS5 Linux images — built from latest \`main\`."
echo ""
echo "Kernel: \`$KVER\`"
echo "Patches: [\`$PSHA\`](https://github.com/ps5-linux/ps5-linux-patches/commit/$PSHA)"
echo "Built: \`$TS\`"
echo ""
echo "| Image | Download |"
echo "|-------|----------|"
echo "| Ubuntu 26.04 | [\`ps5-ubuntu2604.img.xz\`](https://pub-561df4012f1a46fbbdf618d5cc5941f6.r2.dev/ps5-ubuntu2604.img.xz) |"
echo "| Arch | [\`ps5-arch.img.xz\`](https://pub-561df4012f1a46fbbdf618d5cc5941f6.r2.dev/ps5-arch.img.xz) |"
echo "| CachyOS | [\`ps5-cachyos.img.xz\`](https://pub-561df4012f1a46fbbdf618d5cc5941f6.r2.dev/ps5-cachyos.img.xz) |"
echo ""
echo "**SHA256 checksums:**"
echo "\`\`\`"
echo "$SUMS"
echo "\`\`\`"
echo 'EOF'
} >> "$GITHUB_OUTPUT"
- name: Verify release body
run: |
echo "=== Release body ==="
cat <<'EOF'
${{ steps.body.outputs.text }}
EOF
echo ""
echo "=== All checksums ==="
cat meta/*.sha256
cleanup-remote:
needs: release
runs-on: self-hosted
if: always()
steps:
- name: Clean up R2 test files
env:
RCLONE_CONFIG_R2_TYPE: s3
RCLONE_CONFIG_R2_PROVIDER: Cloudflare
RCLONE_CONFIG_R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
RCLONE_CONFIG_R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
RCLONE_CONFIG_R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
RCLONE_CONFIG_R2_NO_CHECK_BUCKET: true
run: |
for d in ubuntu2604 arch cachyos; do
rclone delete "r2:${{ secrets.R2_BUCKET }}/test-${d}.img.xz" -v || true
done
- name: Clean up remote test files
env:
SSH_KEY: ${{ secrets.REMOTE_SSH_KEY }}
SSH_CONFIG: ${{ secrets.REMOTE_SSH_CONFIG }}
SSH_KNOWN_HOSTS: ${{ secrets.REMOTE_SSH_KNOWN_HOSTS }}
run: |
DIR=$(mktemp -d)
echo "$SSH_KEY" > "$DIR/key"
chmod 600 "$DIR/key"
echo "$SSH_CONFIG" > "$DIR/config"
sed -i "s|~/.ssh/remote_key|$DIR/key|" "$DIR/config"
echo "$SSH_KNOWN_HOSTS" > "$DIR/known_hosts"
SSH="ssh -F $DIR/config -o UserKnownHostsFile=$DIR/known_hosts"
$SSH remote rm -rf ./test-upload
rm -rf "$DIR"
+26
View File
@@ -0,0 +1,26 @@
name: deploy
on:
workflow_dispatch:
push:
branches: [main]
pull_request_target:
types: [labeled]
branches: [main]
jobs:
image:
if: github.event.label.name == 'run-ci' || github.event_name != 'pull_request_target'
permissions:
contents: write
uses: ./.github/workflows/build-image.yml
with:
distro: all
secrets:
REMOTE_SSH_KEY: ${{ secrets.REMOTE_SSH_KEY }}
REMOTE_SSH_CONFIG: ${{ secrets.REMOTE_SSH_CONFIG }}
REMOTE_SSH_KNOWN_HOSTS: ${{ secrets.REMOTE_SSH_KNOWN_HOSTS }}
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
R2_BUCKET: ${{ secrets.R2_BUCKET }}
+88 -14
View File
@@ -1,11 +1,13 @@
# PS5 Linux Image Builder
Builds bootable Linux USB images for PlayStation 5 using Docker containers. Supports Ubuntu 26.04, Arch, CachyOS (Gamescope + Steam), and Alpine, individually or as a multi-distro image with kexec switching.
Builds bootable Linux USB images for PlayStation 5 using Docker containers. Supports Ubuntu 26.04, Arch, CachyOS (Gamescope + Steam), and full Kali Linux, individually or as a multi-distro image with kexec switching.
## Prerequisites
- Docker (with permission to run `--privileged` containers) — install as per your distro's instructions
- ~30GB free disk space
- ~30GB free disk space for Ubuntu, Arch, or CachyOS; a full Kali build needs
substantial working space because it creates a 96GB image and a full rootfs
tree (`~150GB` free recommended for a clean Kali build)
Once Docker is installed, add your user to the docker group and apply it without logging out:
@@ -27,7 +29,12 @@ OR
OR
# Build a multi-distro image (ubuntu2604 + arch + alpine + cachyos)
# Build Kali Linux (XFCE + kali-linux-everything)
./build_image.sh --distro kali
OR
# Build a multi-distro image (ubuntu2604 + arch + cachyos)
./build_image.sh --distro all
```
@@ -39,16 +46,84 @@ The script auto-clones the kernel source, applies PS5 patches, compiles, and bui
sudo dd if=output/ps5-ubuntu2604.img of=/dev/sdX bs=4M status=progress
```
## Kali First Boot Time Sync
The Kali image uses UTC by default and enables `ntpsec`. PS5 hardware may boot
Linux without a correct real-time clock, so the displayed time can be wrong
until a network connection is available. The Kali recipe configures IPv4 NTP
sources that were validated through Android USB tethering, because that
connection may not provide usable IPv6 routing.
The Xfce clock's **Time and Date** window is the legacy `time-admin` utility.
On Kali it can report that NTP support is not installed even though `ntpsec`
is installed and active. Verify or repair synchronization from a terminal:
```bash
systemctl --no-pager status ntpsec
ntpq -pn
timedatectl status
```
If the PS5 clock is still wrong after the internet connection is active, force
one initial correction and restart continuous synchronization:
```bash
sudo systemctl stop ntpsec
sudo ntpd -gq -c /etc/ntpsec/ntp.conf
sudo systemctl start ntpsec
date
```
To use a local timezone after boot, for example Kentucky:
```bash
sudo timedatectl set-timezone America/Kentucky/Louisville
timedatectl status
```
## Kali Internal WiFi
The Kali image builds and installs the PS5 IW620 `mwifiex` modules for the
PS5-patched kernel. On boot, `ps5-iw620.service` copies the console-specific
firmware dumped by `ps5-linux-loader` from `/boot/efi/lib/nxp/` into
`/lib/firmware/nxp/`, loads the driver, and enables WiFi radio. If the firmware
payload is present, NetworkManager should expose the internal interface as
`mlan0` and the Xfce network applet can be used to scan and connect.
The Kali desktop autologin is enabled for local first boot. SSH is installed
but disabled by default because the initial local account is `kali` with
password `kali`. Before enabling remote access, change that password:
```bash
passwd
sudo systemctl enable --now ssh
```
The image holds its installed kernel packages and protects the boot-copy hook
from deploying a generic Kali kernel. Do not replace or unhold the PS5 kernel
unless you are intentionally testing a new PS5-patched kernel build.
Ghidra is configured to use JDK 21, its documented supported runtime. Full
Kali installations may also contain newer Java versions for other software.
`kali-linux-everything` installs NFS client components, but the PS5-patched
kernel has NFS disabled. A failed `run-rpc_pipefs.mount` unit can therefore be
reported at boot; it only indicates that NFS mounts are unavailable. The Kali
desktop and security tools are unaffected.
The full Kali toolset also enables `chkrootkit.timer`; its daily integrity scan
can use noticeable CPU time while it runs.
## Options
| Flag | Description | Default |
|------|-------------|---------|
| `--distro` | `ubuntu2604`, `arch`, `cachyos`, `alpine`, or `all` | `ubuntu2604` |
| `--kernel` | Path to kernel source directory | auto-clone `v6.19.10` |
| `--img-size` | Disk image size in MB | `12000` (`32000` for `all`) |
| `--distro` | `ubuntu2604`, `arch`, `cachyos`, `kali`, or `all` | `ubuntu2604` |
| `--kernel` | Path to kernel source directory | auto-clone version selected by PS5 patch set |
| `--img-size` | Disk image size in MB | `12000` (`32000` for `all`, `98304` for `kali`) |
| `--clean` | Remove all cached build artifacts and start fresh | off |
| `--kernel-only` | Build and package the kernel only, then exit | off |
| `--patches-ref` | Branch, tag, or commit SHA for patches | `v1.0` |
| `--patches-ref` | Branch, tag, or commit SHA for patches | `v1.2` |
## Caching
@@ -66,7 +141,7 @@ Use `--clean` to wipe everything and rebuild from scratch. The build will also s
PS5 Linux Image Builder
=======================
Distro: all
(ubuntu2604 arch alpine cachyos)
(ubuntu2604 arch cachyos)
Image size: 32000MB
Kernel src: /path/to/work/linux
@@ -91,19 +166,18 @@ All verbose output goes to `build.log`. The terminal shows a spinner with live p
| Ubuntu 26.04 (Resolute) | GNOME | `.deb` | systemd |
| Arch | Sway | `.pkg.tar.zst` | systemd |
| CachyOS | Gamescope + Steam Big Picture (Arch + `[cachyos]` repo, no v3 migration in image build) | `.pkg.tar.zst` | systemd |
| Alpine (3.21) | GNOME | extracted from `.deb` | OpenRC |
| Kali Linux Rolling | XFCE + `kali-linux-everything` | `.deb` | systemd |
## Multi-distro Image
`--distro all` builds a 32GB image with 5 partitions (one EFI boot partition plus four root filesystems):
`--distro all` builds a 32GB image with 4 partitions (one EFI boot partition plus three root filesystems):
| Partition | Type | Label | Content |
|-----------|------|-------|---------|
| p1 | FAT32 | boot | Shared kernel, per-distro initrds, kexec scripts |
| p2 | ext4 | ubuntu2604 | Ubuntu 26.04 rootfs |
| p3 | ext4 | arch | Arch rootfs |
| p4 | ext4 | alpine | Alpine rootfs |
| p5 | ext4 | cachyos | CachyOS rootfs |
| p4 | ext4 | cachyos | CachyOS rootfs |
The boot partition contains kexec scripts to switch between distros at runtime. Ubuntu 26.04 is the default boot target.
@@ -139,12 +213,12 @@ distros/
ubuntu2604/ # Ubuntu 26.04 (Resolute)
arch/ # Arch Linux
cachyos/ # CachyOS repos + Gamescope/Steam
alpine/ # Alpine 3.21
kali/ # Kali Linux Rolling
shared/ # Kernel postinst hooks (single + multi)
boot/
cmdline.txt # Kernel cmdline template (__DISTRO__ placeholder)
vram.txt # VRAM allocation
kexec-{ubuntu2604,arch,alpine,cachyos}.sh
kexec-{ubuntu2604,arch,cachyos}.sh
work/ # Build artifacts (auto-created)
linux-bin/ # Compiled kernel packages
output/ # Final .img files
-6
View File
@@ -1,6 +0,0 @@
#!/bin/sh
# Switch to Alpine Linux via kexec
set -e
BOOT=/boot/efi
kexec -l "$BOOT/bzImage" --initrd="$BOOT/initrd-alpine.img" --command-line="$(cat $BOOT/cmdline-alpine.txt)"
kexec -e
+15 -9
View File
@@ -9,20 +9,21 @@ KERNEL_SRC=""
CLEAN=false
IMG_SIZE=12000
KERNEL_ONLY=false
PATCHES_REF="v1.0"
PATCHES_REF="v1.2"
MULTI_DISTROS="ubuntu2604 arch alpine cachyos"
MULTI_DISTROS="ubuntu2604 arch cachyos"
usage() {
echo "Usage: $0 [--distro <distro>] [--kernel <path>] [--img-size <MB>] [--clean]"
echo ""
echo "Options:"
echo " --distro Distribution to build: ubuntu2604, arch, cachyos, alpine, all (default: ubuntu2604)"
echo " --distro Distribution to build: ubuntu2604, arch, cachyos, kali, all (default: ubuntu2604)"
echo " --kernel Path to kernel source directory (default: auto-clone to work/linux/)"
echo " --img-size Disk image size in MB (default: 12000, 32000 for --distro all)"
echo " --img-size Disk image size in MB (default: 12000, 32000 for --distro all, 98304 for kali)"
echo " --clean Remove all cached build artifacts and start from scratch"
echo " --clean-only Remove all cached build artifacts and exit"
echo " --kernel-only Build and package the kernel only, then exit"
echo " --patches-ref Branch, tag, or commit SHA for patches (default: v1.0)"
echo " --patches-ref Branch, tag, or commit SHA for patches (default: v1.2)"
exit 1
}
@@ -32,6 +33,7 @@ while [[ $# -gt 0 ]]; do
--kernel) KERNEL_SRC="$2"; shift 2 ;;
--img-size) IMG_SIZE="$2"; shift 2 ;;
--clean) CLEAN=true; shift ;;
--clean-only) CLEAN=true; CLEAN_EXIT=true; shift ;;
--kernel-only) KERNEL_ONLY=true; shift ;;
--patches-ref) [ -n "$2" ] && PATCHES_REF="$2"; shift 2 ;;
-h|--help) usage ;;
@@ -62,9 +64,12 @@ DOCKER_NAME="ps5-build-$$"
if [ "$DISTRO" = "all" ] && [ "$IMG_SIZE" = "12000" ]; then
IMG_SIZE=32000
fi
if [ "$DISTRO" = "kali" ] && [ "$IMG_SIZE" = "12000" ]; then
IMG_SIZE=98304
fi
if [ -z "$FORMAT" ]; then
case "$DISTRO" in arch) FORMAT="arch" ;; all) FORMAT="all" ;; *) FORMAT="deb" ;; esac
case "$DISTRO" in arch|cachyos) FORMAT="arch" ;; all) FORMAT="all" ;; *) FORMAT="deb" ;; esac
fi
KERNEL_BUILDER_PLATFORM="linux/amd64"
@@ -76,14 +81,14 @@ esac
BUILD_PID=""
cleanup() {
trap - INT TERM EXIT
echo ""
echo "Interrupted. Cleaning up..."
echo "Cleaning up..."
docker kill "$DOCKER_NAME" 2>/dev/null || true
[ -n "$BUILD_PID" ] && kill "$BUILD_PID" 2>/dev/null || true
wait "$BUILD_PID" 2>/dev/null || true
exit 130
}
trap cleanup INT TERM
trap cleanup INT TERM EXIT
# --- Clean ---
if [ "$CLEAN" = true ]; then
@@ -94,6 +99,7 @@ if [ "$CLEAN" = true ]; then
alpine rm -rf "/parent/$(basename "$dir")"
done
echo "Done."
[ "$CLEAN_EXIT" = true ] && exit 0
echo ""
fi
-14
View File
@@ -1,14 +0,0 @@
#!/bin/sh
# Grows the root partition and filesystem to fill the disk.
# Runs once on first boot, then disables itself.
set -e
ROOT_DEV=$(findmnt -no SOURCE /)
DISK=$(lsblk -ndo PKNAME "$ROOT_DEV")
PART_NUM=$(cat /sys/class/block/$(basename "$ROOT_DEV")/partition)
growpart "/dev/$DISK" "$PART_NUM" || true
partprobe "/dev/$DISK"
resize2fs "$ROOT_DEV" || true
rc-update del grow-rootfs default
-13
View File
@@ -1,13 +0,0 @@
#!/sbin/openrc-run
description="Grow root filesystem to fill disk"
depend() {
need localmount
}
start() {
ebegin "Growing root filesystem"
/usr/local/sbin/grow-rootfs
eend $?
}
-175
View File
@@ -1,175 +0,0 @@
image:
name: ps5-alpine
distribution: alpine
release: "3.21"
description: Alpine Linux with Weston
architecture: x86_64
source:
downloader: alpinelinux-http
url: https://dl-cdn.alpinelinux.org/alpine
skip_verification: true
packages:
manager: apk
update: true
cleanup: true
repositories:
- name: repositories
url: |-
https://dl-cdn.alpinelinux.org/alpine/v3.21/main
https://dl-cdn.alpinelinux.org/alpine/v3.21/community
sets:
- packages:
# Wayland compositor + basics
- weston
- weston-shell-desktop
- weston-backend-drm
- weston-xwayland
- weston-terminal
- foot
- xwayland
# Wayland support
- dbus
- eudev
- elogind
- polkit-elogind
- seatd
# Audio / media
- pipewire
- wireplumber
- pipewire-pulse
# Display / GPU
- mesa-gbm
- mesa-egl
- mesa-gl
- mesa-dri-gallium
- mesa-va-gallium
- mesa-vulkan-ati
- libinput
- xkeyboard-config
# Networking
- networkmanager
- networkmanager-wifi
- wpa_supplicant
- linux-firmware
- linux-firmware-amdgpu
- xf86-video-amdgpu
# System
- openrc
- openssh
- sudo
- shadow
- nano
- bash
- coreutils
- util-linux
- e2fsprogs
- e2fsprogs-extra
- cloud-utils-growpart
- mkinitfs
- kmod
- kexec-tools
- syslog-ng
# Fonts
- font-ubuntu
- font-liberation
- font-dejavu
- fontconfig
action: install
files:
- path: /etc/hostname
generator: hostname
- path: /etc/hosts
generator: hosts
- path: /etc/kernel/postinst.d/zz-update-boot
generator: copy
source: /tmp/build-staging/zz-update-boot
mode: "0755"
- path: /etc/fstab
generator: copy
source: /tmp/build-staging/fstab
- path: /usr/local/sbin/grow-rootfs
generator: copy
source: /tmp/build-staging/grow-rootfs
mode: "0755"
- path: /etc/init.d/grow-rootfs
generator: copy
source: /tmp/build-staging/grow-rootfs.openrc
mode: "0755"
actions:
- trigger: post-unpack
action: |-
#!/bin/sh
set -eux
# Retry up to 10 times on transient network errors
echo "retries = 10" >> /etc/apk/apk.conf 2>/dev/null || true
# dbus post-install chokes on an empty machine-id
rm -f /etc/machine-id
- trigger: post-packages
action: |-
#!/bin/sh
set -eux
fc-cache -f -v
# SSH
sed -i 's/^#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config
rc-update add syslog-ng default
rc-update add udev sysinit
rc-update add udev-trigger sysinit
rc-update add udev-settle sysinit
rc-update add sshd default
rc-update add dbus default
rc-update add elogind default
rc-update add seatd default
rc-update add networkmanager default
- trigger: post-files
action: |-
#!/bin/sh
set -eux
rc-update add grow-rootfs default
# Create default user (ps5/ps5) in groups needed for Wayland/input
addgroup ps5
adduser -D -s /bin/bash -G ps5 ps5
echo "ps5:ps5" | chpasswd
addgroup ps5 wheel
addgroup ps5 video
addgroup ps5 input
addgroup ps5 seat
echo "%wheel ALL=(ALL:ALL) NOPASSWD: ALL" > /etc/sudoers.d/wheel
# Auto-start weston on tty1 login
{
echo 'if [ "$(tty)" = "/dev/tty1" ] && [ -z "$WAYLAND_DISPLAY" ]; then'
echo ' export XDG_RUNTIME_DIR="/tmp/xdg-runtime-$(id -u)"'
echo ' mkdir -p "$XDG_RUNTIME_DIR"'
echo ' chmod 700 "$XDG_RUNTIME_DIR"'
echo ' exec weston --backend=drm-backend.so'
echo 'fi'
} >> /home/ps5/.bash_profile
chown -R ps5:ps5 /home/ps5
mkdir -p /boot/efi
mappings:
architecture_map: alpinelinux
+28 -10
View File
@@ -118,13 +118,19 @@ actions:
action: |-
#!/bin/bash
set -eux
# Pacman 7 Landlock sandbox is unsupported under QEMU; disable it.
sed -i '/^\[options\]/a DisableSandbox' /etc/pacman.conf
# Initialise pacman keyring
pacman-key --init
pacman-key --populate archlinux
# Fetch up-to-date HTTPS mirrors ranked by score, force-refresh package DBs
curl -fsSL "https://archlinux.org/mirrorlist/?country=all&protocol=https&use_mirror_status=on" \
| sed 's/^#Server/Server/' > /etc/pacman.d/mirrorlist
# Keep CI on a small stable mirror set. The full active global list can
# select slow mirrors and fail pacman's low-speed download timeout.
cat > /etc/pacman.d/mirrorlist <<'EOF'
Server = https://geo.mirror.pkgbuild.com/$repo/os/$arch
Server = https://mirrors.kernel.org/archlinux/$repo/os/$arch
EOF
pacman -Syy --noconfirm
@@ -132,21 +138,33 @@ actions:
action: |-
#!/bin/bash
set -eux
fc-cache -f -v
# fc-cache / systemctl can SIGSEGV under QEMU when image plat is linux/amd64 on arm64 hosts.
fc-cache -f 2>/dev/null || true
symlink_wants() {
local src=$1
local target=$2
[ -e "$src" ] || return 0
mkdir -p "/etc/systemd/system/${target}.wants"
ln -sf "$src" "/etc/systemd/system/${target}.wants/$(basename "$src")"
}
# SSH
sed -i 's/^#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config
systemctl enable sshd
systemctl enable NetworkManager
systemctl enable seatd
systemctl enable pipewire pipewire-pulse wireplumber || true
symlink_wants /usr/lib/systemd/system/sshd.service multi-user.target
symlink_wants /usr/lib/systemd/system/NetworkManager.service multi-user.target
symlink_wants /usr/lib/systemd/system/seatd.service multi-user.target
for s in pipewire pipewire-pulse wireplumber; do
symlink_wants "/usr/lib/systemd/system/${s}.service" default.target || true
done
- trigger: post-files
action: |-
#!/bin/bash
set -eux
systemctl enable grow-rootfs.service
mkdir -p /etc/systemd/system/local-fs.target.wants
ln -sf /etc/systemd/system/grow-rootfs.service \
/etc/systemd/system/local-fs.target.wants/grow-rootfs.service
# Create default user (ps5/ps5)
useradd -m -G wheel,seat -s /bin/bash ps5
+84 -51
View File
@@ -1,11 +1,8 @@
#!/bin/bash
# First-boot interactive setup for CachyOS PS5 image.
# Sets the password for the 'steam' user and optionally the hostname,
# then creates a sentinel so this script never runs again.
set -e
# First-boot: dialog wizard on tty1 (timezone, steam password, hostname).
# Defaults: UTC, ps5, keep image password (steam:steam). Skips on Cancel use defaults.
set -euo pipefail
# Keep interactive prompts readable on tty1 by suppressing kernel console noise
# during setup. Restore the previous setting before exiting.
ORIG_PRINTK="$(cat /proc/sys/kernel/printk 2>/dev/null || true)"
if [ -n "$ORIG_PRINTK" ]; then
echo "1 4 1 7" > /proc/sys/kernel/printk || true
@@ -17,57 +14,93 @@ cleanup_printk() {
}
trap cleanup_printk EXIT
clear
echo "=================================================="
echo " Welcome to CachyOS on PS5!"
echo "=================================================="
echo
echo "User: steam (fixed — used for both Gaming Mode and Desktop)"
echo
export TERM="${TERM:-linux}"
# --- Password ---
while true; do
read -rsp "Set a password for 'steam': " PASSWORD; echo
read -rsp "Confirm password: " PASSWORD2; echo
if [[ -z "$PASSWORD" ]]; then
echo "Password cannot be empty."
elif [[ "$PASSWORD" != "$PASSWORD2" ]]; then
echo "Passwords do not match. Try again."
apply_timezone() {
local tz="${1:-UTC}"
timedatectl set-timezone "$tz" 2>/dev/null || ln -sf "/usr/share/zoneinfo/$tz" /etc/localtime
}
apply_hostname() {
local hn="${1:-ps5}"
hostnamectl set-hostname "$hn" 2>/dev/null || echo "$hn" > /etc/hostname
if grep -q '^127\.0\.1\.1' /etc/hosts; then
sed -i "s/^127\.0\.1\.1.*/127.0.1.1 $hn/" /etc/hosts
else
break
echo "127.0.1.1 $hn" >> /etc/hosts
fi
done
}
# --- Hostname ---
read -rp "Hostname [ps5]: " HOSTNAME
HOSTNAME="${HOSTNAME:-ps5}"
main() {
if ! command -v dialog >/dev/null 2>&1; then
echo "first-boot-setup: dialog not installed" >&2
exit 1
fi
echo
echo "Applying settings..."
clear
echo "steam:$PASSWORD" | chpasswd
local choice
choice="$(dialog --stdout --clear --menu \
"Welcome to CachyOS on PS5\nUser: steam (Gaming Mode + Desktop)" \
17 64 4 \
defaults "Use defaults (UTC, hostname ps5, keep password)" \
custom "Customize timezone, password, hostname" )" \
|| choice="defaults"
hostnamectl set-hostname "$HOSTNAME" 2>/dev/null || echo "$HOSTNAME" > /etc/hostname
# Update /etc/hosts if a 127.0.1.1 line exists, otherwise append one.
if grep -q '^127\.0\.1\.1' /etc/hosts; then
sed -i "s/^127\.0\.1\.1.*/127.0.1.1 $HOSTNAME/" /etc/hosts
else
echo "127.0.1.1 $HOSTNAME" >> /etc/hosts
fi
if [[ "$choice" == "defaults" ]]; then
apply_timezone UTC
apply_hostname ps5
else
local zones
mapfile -t zones < <(timedatectl list-timezones 2>/dev/null | LC_ALL=C sort -u)
local -a menu_args=()
local z
for z in "${zones[@]}"; do
menu_args+=("$z" "$z")
done
# Sentinel — prevents this service from running on subsequent boots.
touch /etc/ps5-first-boot-done
local tz_pick
if ((${#menu_args[@]} == 0)); then
apply_timezone UTC
elif tz_pick="$(dialog --stdout --clear --menu "Select timezone (Cancel = UTC)" 22 70 15 \
"${menu_args[@]}")"; then
apply_timezone "$tz_pick"
else
apply_timezone UTC
fi
echo
echo "=================================================="
echo " Setup complete!"
echo "=================================================="
echo
echo "The system will log in on tty1 and start Steam Gaming Mode (Gamescope)."
echo "(No display manager — same pattern as the Arch Sway image in this repo.)"
echo
echo "Tips:"
echo " In Steam: Power > Switch to Desktop → KDE Plasma (X11)"
echo " In KDE: Double-click 'Return to Gaming Mode' on the desktop"
echo
sleep 3
if dialog --clear --yesno "Change password for user steam?" 8 55; then
local p1 p2
while true; do
p1="$(dialog --stdout --clear --insecure --passwordbox "New password (empty = skip)" 9 55)" \
|| break
p2="$(dialog --stdout --clear --insecure --passwordbox "Confirm password" 9 55)" \
|| break
if [[ -z "$p1" ]]; then
dialog --clear --msgbox "Password unchanged (empty)." 7 44
break
fi
if [[ "$p1" != "$p2" ]]; then
dialog --clear --msgbox "Passwords do not match. Try again." 7 50
continue
fi
echo "steam:$p1" | chpasswd
break
done
fi
local hn
hn="$(dialog --stdout --clear --inputbox "Hostname (empty = ps5)" 9 50 "ps5")" \
|| hn="ps5"
hn="${hn:-ps5}"
apply_hostname "$hn"
fi
touch /etc/ps5-first-boot-done
dialog --clear --msgbox \
"Setup complete.\n\nYou will log in on tty1 (Steam Gaming Mode).\n\nDesktop: Steam > Power > Switch to KDE\nReturn: double-click the desktop shortcut." \
14 62
}
main "$@"
+17 -9
View File
@@ -59,6 +59,11 @@ packages:
# Networking
- networkmanager
- linux-firmware
# GIO module libgiognutls.so (KDE/Konsole, GVfs, etc.): needs gnutls → nettle (libhogweed).
# Explicit install avoids a sparse dependency tree under distrobuilder cleanup: true.
- gnutls
- nettle
- glib-networking
# System
- base
@@ -75,6 +80,7 @@ packages:
- dbus
- kexec-tools
- curl
- dialog
# Fonts (Steam UI + KDE) — CJK + emoji avoid "tofu" boxes in Steam Big Picture language list
- ttf-dejavu
@@ -156,12 +162,8 @@ actions:
action: |-
#!/bin/bash
set -eux
# Pacman 7 Landlock/seccomp sandbox breaks under distrobuilder/Docker (unsupported kernel).
if grep -q '^#DisableSandbox' /etc/pacman.conf; then
sed -i 's/^#DisableSandbox/DisableSandbox/' /etc/pacman.conf
elif ! grep -q '^DisableSandbox' /etc/pacman.conf; then
sed -i '/^\[options\]/a DisableSandbox' /etc/pacman.conf
fi
# Pacman 7 Landlock sandbox is unsupported under QEMU; disable it.
sed -i '/^\[options\]/a DisableSandbox' /etc/pacman.conf
CACHYOS_PKG="https://mirror.cachyos.org/repo/x86_64/cachyos"
@@ -184,8 +186,12 @@ actions:
# Enable #[multilib] / #Include block (\\\\ in YAML would break the sed address regex)
sed -i '/^#\[multilib\]/,/^#Include/s/^#//' /etc/pacman.conf
curl -fsSL "https://archlinux.org/mirrorlist/?country=all&protocol=https&use_mirror_status=on" \
| sed 's/^#Server/Server/' > /etc/pacman.d/mirrorlist
# Keep CI on a small stable mirror set. The full active global list can
# select slow mirrors and fail pacman's low-speed download timeout.
cat > /etc/pacman.d/mirrorlist <<'EOF'
Server = https://geo.mirror.pkgbuild.com/$repo/os/$arch
Server = https://mirrors.kernel.org/archlinux/$repo/os/$arch
EOF
pacman -Syy --noconfirm
@@ -245,6 +251,8 @@ actions:
touch /etc/ps5-use-tty-sessions
ln -sf /usr/lib/systemd/system/multi-user.target /etc/systemd/system/default.target
ln -sf /dev/null /etc/systemd/system/sddm.service
# First-boot wizard handles timezone/password/hostname; avoid duplicate prompts.
ln -sf /dev/null /etc/systemd/system/systemd-firstboot.service
mkdir -p /etc/systemd/system/getty@tty1.service.d
printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=-/sbin/agetty --autologin steam --noclear %I $TERM' \
@@ -322,7 +330,7 @@ actions:
/etc/mkinitcpio.conf
if ls /opt/pkgs/*.pkg.tar.zst 1>/dev/null 2>&1; then
pacman -U --noconfirm /opt/pkgs/*.pkg.tar.zst
pacman -U --noconfirm --ask 4 /opt/pkgs/*.pkg.tar.zst
rm -rf /opt/pkgs
KVER=$(ls -1t /lib/modules | head -1)
mkinitcpio -k "$KVER" -g "/boot/initrd.img-$KVER" || true
+1
View File
@@ -0,0 +1 @@
root=LABEL=__DISTRO__ rw rootwait console=ttyTitania0 console=tty0 mitigations=off idle=halt preempt=full
+19
View File
@@ -0,0 +1,19 @@
#!/bin/bash
# Grows the root partition and filesystem to fill the disk.
# Runs once on first boot, then disables itself.
# If resize fails, the service stays enabled and retries next boot.
ROOT_DEV=$(findmnt -no SOURCE /) || { echo "Cannot find root device"; exit 1; }
DISK=$(lsblk -ndo PKNAME "$ROOT_DEV")
PART_NUM=$(cat /sys/class/block/$(basename "$ROOT_DEV")/partition 2>/dev/null)
if [ -z "$DISK" ] || [ -z "$PART_NUM" ]; then
echo "Cannot determine disk layout (DISK=$DISK PART_NUM=$PART_NUM)"
exit 1
fi
growpart "/dev/$DISK" "$PART_NUM" || true
partprobe "/dev/$DISK"
resize2fs "$ROOT_DEV"
systemctl disable grow-rootfs.service
+10
View File
@@ -0,0 +1,10 @@
[Unit]
Description=Grow root filesystem to fill disk
After=local-fs.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/grow-rootfs
[Install]
WantedBy=multi-user.target
+388
View File
@@ -0,0 +1,388 @@
image:
name: ps5-kali
distribution: debian
# Release images use Kali's last snapshot for stable, repeatable builds.
release: kali-last-snapshot
description: Kali Linux XFCE desktop
architecture: x86_64
source:
downloader: debootstrap
url: https://kali.download/kali
variant: minbase
keyserver: keyserver.ubuntu.com
keys:
- 0x827C8569F2518CC677FECA1AED65462EC8D5E4C5
packages:
manager: apt
update: true
cleanup: true
repositories:
- name: sources.list
url: |-
deb https://kali.download/kali kali-last-snapshot main contrib non-free non-free-firmware
architectures:
- amd64
sets:
- packages:
# Full Kali desktop and toolset
- kali-desktop-xfce
- kali-linux-everything
- kali-defaults
- kali-defaults-desktop
- kali-themes
- kali-wallpapers-2026
- kali-wallpapers-all
- kali-menu
- kali-tweaks
- kali-undercover
# Display / GPU / audio
- lightdm
- lightdm-gtk-greeter
- xserver-xorg
- xserver-xorg-video-amdgpu
- dbus-x11
- x11-xserver-utils
- mesa-vulkan-drivers
- pipewire
- pipewire-pulse
- wireplumber
- pavucontrol
# Networking and remote access
- network-manager
- network-manager-gnome
- openssh-server
- iputils-ping
- ntpsec
- iw
- wpasupplicant
- wireless-regdb
# Firmware and PS5 boot support
- firmware-linux
- firmware-linux-nonfree
- firmware-amd-graphics
- firmware-realtek
- firmware-atheros
- firmware-mediatek
- firmware-misc-nonfree
- initramfs-tools
- kexec-tools
- kmod
- busybox
- zstd
- systemd-zram-generator
# Base system utilities
- sudo
- cloud-guest-utils
- parted
- e2fsprogs
- usbutils
- pciutils
- rfkill
- util-linux
- curl
- wget
- git
- ca-certificates
- build-essential
- bc
- bison
- flex
- libssl-dev
- libelf-dev
- vim
- nano
- tmux
- firefox-esr
# Ghidra 12.1 requires JDK 21. The full Kali set also installs newer JDKs.
- openjdk-21-jdk
action: install
files:
- path: /etc/hostname
generator: hostname
- path: /etc/hosts
generator: hosts
- path: /etc/machine-id
generator: dump
- path: /etc/kernel/postinst.d/zz-update-boot
generator: copy
source: /tmp/build-staging/zz-update-boot
mode: "0755"
- path: /etc/fstab
generator: copy
source: /tmp/build-staging/fstab
- path: /etc/apt/trusted.gpg.d/kali-archive-keyring.asc
generator: copy
source: /tmp/build-staging/kali-archive-keyring.asc
- path: /usr/local/sbin/grow-rootfs
generator: copy
source: /tmp/build-staging/grow-rootfs
mode: "0755"
- path: /etc/systemd/system/grow-rootfs.service
generator: copy
source: /tmp/build-staging/grow-rootfs.service
- path: /opt/debs/
generator: copy
source: /tmp/build-staging/debs
actions:
- trigger: post-unpack
action: |-
#!/bin/bash
set -eux
rm -f /var/lib/apt/lists/*InRelease /var/lib/apt/lists/*Release /var/lib/apt/lists/*Packages* || true
echo 'Acquire::Retries "10";' > /etc/apt/apt.conf.d/80retry
cat > /etc/apt/trusted.gpg.d/kali-archive-keyring.asc <<'EOF'
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBGgBJJUBEADlMTZVDCjrSXIAuYfL3VZt8OoplUdw3mSPlhIjZQmIo2sdzvAF
EMSCQ+vWeD4VqV9tBtiVx6j8VSfyW18YHHAkvajWDRg5hPLf80wGxrtXYu+vj3Ri
5dOMhrl9fHKIifPOoV3pFTtOk0dB9lkcmtNzjWgwOJduLbjjraE1BBKqc0uaXDCa
RJnPYkQuJQcZxmZVFAo9NP7KSAL1zMvutAd0R3WeMaWpT22nGa3rJj4kj25zV6Kn
qGnv5kQaY2cTlQHnp6EbiLe5sCE7zIOp5CjwIJhyCyn4zT8KqGB8Sw8PEi9mYlSY
wbGzzfAAbBk7Y8xbmvRrkrHzU74jH0iMK566QVu2yl3Dz0hrlliV6vGn2ZWu7qmh
lwXSb+q4u46tDbFjdUjYJG2upx5vOm5SewD9snLB4YN2e2qDeQgY16AfpkJa51+u
PwTeDCbfuQu3irLWcGRZgpOBgsxqCtpZBmF6ED7L8tntoyjZ9WeB8FnTcv7hx5J1
IPCO4K5TvW0SX6ZKp1Jusbkn5hrrFTjOJHhIDVdioM/wYDKkqJ9e25oGAqPkJYRY
euonU1teK+EOLM7ZIbalhukrw0bgYl9UJRxQMLEhZzoiiCLLiv3oWHAQGFclP+1E
zXgbLBviFAU4+DMXfhA6vy8BmS9oTpleS1p3/EOwf2rX/yt4qF7IW9ZXuQARAQAB
tEBLYWxpIExpbnV4IEFyY2hpdmUgQXV0b21hdGljIFNpZ25pbmcgS2V5ICgyMDI1
KSA8ZGV2ZWxAa2FsaS5vcmc+iQJUBBMBCgA+FiEEgnyFafJRjMZ3/soa7WVGLsjV
5MUFAmgF7tkCGwMFCQWkfeUFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQ7WVG
LsjV5MXcjw//XeI6OXY7VcH+hXRcT7W49AwqRfmSaSEWs474G2DQR9UppzvkFCab
uiWl5jrlkeGbVFsiBruJfIlCdYMMnPk8gEm/SEhVRqcZVOjYCWcMlSVB6oU+6tgW
jKPPRDELiq7mTl8S4sEdvUxpsWoMqEQZ1+CsJsw+p+TARGNIrUUdL9hTOoOUpvue
nKNEEfzbKvLk2gj2tKOgr1HcDmVbbmRsL87+UYq1JvA0OzJ0KrhBdTZHJWchAJwa
p+UUog2XrzvXYXWBPfQLsNVkFirmVd1B5vonj3OeNlVU51YriRQ4P4onLrwlfha8
vUGeNJw/ihXTQFpvmF7fFSRa7Pr5YfWkDZ4BGuEB+kSycu2PMWCXXHdY++cMIlRf
uUg/wvzcwAkS99DJ0EAiOun0oypE5+r5HwfaI9IrJlgZMPlFctyBIGVg2DFZCdLH
VHG1Voq/CU2tgWvWyuHXHVlUiZiWJoj7BbVa88Gj+VyvB/md1xBh0ScmfH4uGgnX
hpLFPIVuR1SJYarovVmtFhAjbqbrAA4Q9utpOeOOVDMD5tuq856/lLh+SWPkRsUy
ZJTwz1Nh0rJ/UJOMSo4ljkkr53iR/IM4woAAaP+0hkZoIDSbVVW5Im1Yj461exl4
0ltMBMym2KZk/IFOTloSfW7hMmGlqaLfQEH1ryHefIIpkgKJa6WgVxA=
=f+tz
-----END PGP PUBLIC KEY BLOCK-----
EOF
apt-get update
- trigger: post-update
action: |-
#!/bin/bash
set -eux
passwd -l root
- trigger: post-packages
action: |-
#!/bin/bash
set -eux
# Default user (kali/kali)
useradd -m -G sudo -s /bin/bash kali
echo "kali:kali" | chpasswd
# The image is assembled inside Docker, where the generated hostname is
# otherwise the temporary container ID. Use a stable PS5 hostname.
printf 'ps5\n' > /etc/hostname
if grep -q '^127\.0\.1\.1' /etc/hosts; then
sed -i 's/^127\.0\.1\.1.*/127.0.1.1 ps5/' /etc/hosts
else
printf '127.0.1.1 ps5\n' >> /etc/hosts
fi
# Ghidra 12.1 requires JDK 21, while Kali Rolling may select a newer Java
# as the system default. Keep its desktop launcher on the supported JDK.
test -x /usr/lib/jvm/java-21-openjdk-amd64/bin/java
sed -i 's|^JAVA_HOME_OVERRIDE=.*|JAVA_HOME_OVERRIDE=/usr/lib/jvm/java-21-openjdk-amd64|' /usr/share/ghidra/support/launch.properties
# LightDM autologin into XFCE
mkdir -p /etc/lightdm/lightdm.conf.d
cat > /etc/lightdm/lightdm.conf.d/50-ps5-autologin.conf <<'EOF'
[Seat:*]
autologin-user=kali
autologin-user-timeout=0
user-session=xfce
EOF
# SSH is installed for opt-in remote access. Keep it disabled while the
# distributed image still has public default local credentials.
sed -i 's/^#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config
systemctl set-default graphical.target
systemctl disable ssh.service || true
systemctl disable ssh.socket || true
systemctl enable NetworkManager
systemctl enable lightdm
# PS5 systems may boot without a useful hardware clock. Keep the shared
# image timezone-neutral and synchronize after a network connection exists.
ln -sf /usr/share/zoneinfo/Etc/UTC /etc/localtime
printf 'Etc/UTC\n' > /etc/timezone
# Android USB tethering may provide IPv4 only. The default pool returned
# one unreachable IPv4 peer in PS5 testing; these independent IPv4 peers
# responded and preserve multi-source time validation.
sed -i '/^pool [0-3]\.debian\.pool\.ntp\.org /s/^/# PS5 replaced: /' /etc/ntpsec/ntp.conf
cat >> /etc/ntpsec/ntp.conf <<'EOF'
# PS5 USB-tether-friendly IPv4 time sources
server -4 time.cloudflare.com iburst
server -4 time.google.com iburst
server -4 time.apple.com iburst
server -4 time.windows.com iburst
EOF
systemctl enable ntpsec.service
# zram swap: half of RAM, zstd compressed
printf '[zram0]\nzram-size = ram / 2\ncompression-algorithm = zstd\n' > /etc/systemd/zram-generator.conf
# Avoid PS5 display sleep/blanking during first boot demos.
install -d -m 0755 /home/kali/.config/autostart
cat > /home/kali/.config/autostart/ps5-display.desktop <<'EOF'
[Desktop Entry]
Type=Application
Name=PS5 Display Defaults
Exec=sh -c 'xset s off -dpms; xset s noblank'
X-GNOME-Autostart-enabled=true
EOF
chown -R kali:kali /home/kali
- trigger: post-files
action: |-
#!/bin/bash
set -eux
systemctl enable grow-rootfs.service
mkdir -p /boot/efi
# This USB image uses zram, not a disk-backed hibernation device. Avoid
# embedding a temporary build-container device as a resume target.
mkdir -p /etc/initramfs-tools/conf.d
printf 'RESUME=none\n' > /etc/initramfs-tools/conf.d/resume
dpkg -i /opt/debs/*.deb
rm -rf /opt/debs
# kali-linux-everything installs a generic Kali kernel. Keep updates from
# replacing the PS5-patched kernel used by the loader.
dpkg-query -W -f='${binary:Package} ${db:Status-Status}\n' 'linux-image-*' 2>/dev/null \
| awk '$2 == "installed" { print $1 }' \
| xargs -r apt-mark hold
apt-mark hold linux-ps5 || true
KVER=$(ls -1t /lib/modules | head -1)
depmod -a "$KVER"
# PS5 internal WiFi uses the Marvell/NXP IW620 at PCI ID 1b4b:2b56. Build
# the PS5-patched NXP mwifiex module for the installed PS5 kernel. Firmware
# is console-provided by ps5-linux-loader on the boot partition, so a boot
# service copies it into /lib/firmware before loading the module.
IW620_SRC=/tmp/ps5-iw620-mwifiex
IW620_PATCHES=/tmp/ps5-linux-mwifiex
IW620_NXP_REF=a5fe4e194bf99315e349d81d77d6dfacec70757a
IW620_PATCH_REF=5cfd063449f27e2f8a7d17c814a3bb21c27aa903
rm -rf "$IW620_SRC" "$IW620_PATCHES"
git clone https://github.com/nxp-imx/mwifiex.git "$IW620_SRC"
git -C "$IW620_SRC" checkout "$IW620_NXP_REF"
git clone https://github.com/ps5-linux/ps5-linux-mwifiex.git "$IW620_PATCHES"
git -C "$IW620_PATCHES" checkout "$IW620_PATCH_REF"
git -C "$IW620_SRC" apply "$IW620_PATCHES/ps5-iw620.patch"
make -C "$IW620_SRC" CONFIG_OBJTOOL= KERNELDIR="/lib/modules/$KVER/build" ARCH=x86 -j"$(nproc)"
test -f "$IW620_SRC/mlan.ko"
test -f "$IW620_SRC/moal.ko"
install -d "/lib/modules/$KVER/extra/ps5-iw620"
install -m 0644 "$IW620_SRC/mlan.ko" "/lib/modules/$KVER/extra/ps5-iw620/mlan.ko"
install -m 0644 "$IW620_SRC/moal.ko" "/lib/modules/$KVER/extra/ps5-iw620/moal.ko"
install -d /etc/modprobe.d /usr/local/sbin /etc/systemd/system /usr/share/doc/ps5-iw620
cat > /etc/modprobe.d/ps5-iw620.conf <<'EOF'
# PS5 IW620 mwifiex
# Prevent udev from auto-loading moal before the loader-provided firmware is
# copied from the boot partition. ps5-iw620.service loads it explicitly.
blacklist moal
blacklist mlan
softdep moal pre: cfg80211 mlan
options moal fw_name=nxp/pcieuartiw620_combo_v1.bin pcie_int_mode=1 drv_mode=1 cfg80211_wext=4 sta_name=mlan ext_scan=0 auto_fw_reload=0 wifi_reset_config=0 sched_scan=0 ps_mode=2 auto_ds=2 amsdu_disable=1
EOF
cat > /usr/local/sbin/ps5-iw620-load <<'EOF'
#!/bin/sh
set -eu
FW_NAME=nxp/pcieuartiw620_combo_v1.bin
FW_DST=/lib/firmware/$FW_NAME
FW_SRC=
for candidate in "/boot/efi/lib/$FW_NAME" "/boot/lib/$FW_NAME" "$FW_DST"; do
if [ -f "$candidate" ]; then
FW_SRC="$candidate"
break
fi
done
if [ -z "$FW_SRC" ]; then
FW_SRC="$(find /boot /boot/efi -path "*/$FW_NAME" -type f 2>/dev/null | head -n 1)"
fi
if [ -n "$FW_SRC" ] && [ -f "$FW_SRC" ]; then
install -D -m 0644 "$FW_SRC" "$FW_DST"
fi
if [ ! -f "$FW_DST" ]; then
echo "PS5 IW620 firmware not found on the boot partition; skipping WiFi load"
exit 0
fi
# If udev raced us and loaded the module before firmware was copied, reload
# it so firmware init can succeed.
modprobe -r moal mlan 2>/dev/null || true
modprobe cfg80211 || true
modprobe moal
rfkill unblock all 2>/dev/null || true
nmcli radio wifi on 2>/dev/null || true
EOF
chmod 0755 /usr/local/sbin/ps5-iw620-load
cat > /etc/systemd/system/ps5-iw620.service <<'EOF'
[Unit]
Description=Load PS5 IW620 internal WiFi
RequiresMountsFor=/boot/efi
Wants=systemd-udev-settle.service
After=local-fs.target boot-efi.mount systemd-udev-settle.service
Before=NetworkManager.service network-pre.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/ps5-iw620-load
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
cat > /usr/share/doc/ps5-iw620/BUILDINFO <<EOF
NXP mwifiex ref: $IW620_NXP_REF
ps5-linux-mwifiex ref: $IW620_PATCH_REF
Kernel: $KVER
Module options: fw_name=nxp/pcieuartiw620_combo_v1.bin pcie_int_mode=1 drv_mode=1 cfg80211_wext=4 sta_name=mlan ext_scan=0 auto_fw_reload=0 wifi_reset_config=0 sched_scan=0 ps_mode=2 auto_ds=2 amsdu_disable=1
EOF
depmod -a "$KVER"
systemctl enable ps5-iw620.service
rm -rf "$IW620_SRC" "$IW620_PATCHES"
grep -qxF amdgpu /etc/initramfs-tools/modules || printf '\namdgpu\n' >> /etc/initramfs-tools/modules
printf 'MODULES=most\nBUSYBOX=y\n' > /etc/initramfs-tools/conf.d/ps5-amdgpu
update-initramfs -c -k "$KVER"
/etc/kernel/postinst.d/zz-update-boot "$KVER"
mappings:
architecture_map: debian
+29
View File
@@ -0,0 +1,29 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBGgBJJUBEADlMTZVDCjrSXIAuYfL3VZt8OoplUdw3mSPlhIjZQmIo2sdzvAF
EMSCQ+vWeD4VqV9tBtiVx6j8VSfyW18YHHAkvajWDRg5hPLf80wGxrtXYu+vj3Ri
5dOMhrl9fHKIifPOoV3pFTtOk0dB9lkcmtNzjWgwOJduLbjjraE1BBKqc0uaXDCa
RJnPYkQuJQcZxmZVFAo9NP7KSAL1zMvutAd0R3WeMaWpT22nGa3rJj4kj25zV6Kn
qGnv5kQaY2cTlQHnp6EbiLe5sCE7zIOp5CjwIJhyCyn4zT8KqGB8Sw8PEi9mYlSY
wbGzzfAAbBk7Y8xbmvRrkrHzU74jH0iMK566QVu2yl3Dz0hrlliV6vGn2ZWu7qmh
lwXSb+q4u46tDbFjdUjYJG2upx5vOm5SewD9snLB4YN2e2qDeQgY16AfpkJa51+u
PwTeDCbfuQu3irLWcGRZgpOBgsxqCtpZBmF6ED7L8tntoyjZ9WeB8FnTcv7hx5J1
IPCO4K5TvW0SX6ZKp1Jusbkn5hrrFTjOJHhIDVdioM/wYDKkqJ9e25oGAqPkJYRY
euonU1teK+EOLM7ZIbalhukrw0bgYl9UJRxQMLEhZzoiiCLLiv3oWHAQGFclP+1E
zXgbLBviFAU4+DMXfhA6vy8BmS9oTpleS1p3/EOwf2rX/yt4qF7IW9ZXuQARAQAB
tEBLYWxpIExpbnV4IEFyY2hpdmUgQXV0b21hdGljIFNpZ25pbmcgS2V5ICgyMDI1
KSA8ZGV2ZWxAa2FsaS5vcmc+iQJUBBMBCgA+FiEEgnyFafJRjMZ3/soa7WVGLsjV
5MUFAmgF7tkCGwMFCQWkfeUFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQ7WVG
LsjV5MXcjw//XeI6OXY7VcH+hXRcT7W49AwqRfmSaSEWs474G2DQR9UppzvkFCab
uiWl5jrlkeGbVFsiBruJfIlCdYMMnPk8gEm/SEhVRqcZVOjYCWcMlSVB6oU+6tgW
jKPPRDELiq7mTl8S4sEdvUxpsWoMqEQZ1+CsJsw+p+TARGNIrUUdL9hTOoOUpvue
nKNEEfzbKvLk2gj2tKOgr1HcDmVbbmRsL87+UYq1JvA0OzJ0KrhBdTZHJWchAJwa
p+UUog2XrzvXYXWBPfQLsNVkFirmVd1B5vonj3OeNlVU51YriRQ4P4onLrwlfha8
vUGeNJw/ihXTQFpvmF7fFSRa7Pr5YfWkDZ4BGuEB+kSycu2PMWCXXHdY++cMIlRf
uUg/wvzcwAkS99DJ0EAiOun0oypE5+r5HwfaI9IrJlgZMPlFctyBIGVg2DFZCdLH
VHG1Voq/CU2tgWvWyuHXHVlUiZiWJoj7BbVa88Gj+VyvB/md1xBh0ScmfH4uGgnX
hpLFPIVuR1SJYarovVmtFhAjbqbrAA4Q9utpOeOOVDMD5tuq856/lLh+SWPkRsUy
ZJTwz1Nh0rJ/UJOMSo4ljkkr53iR/IM4woAAaP+0hkZoIDSbVVW5Im1Yj461exl4
0ltMBMym2KZk/IFOTloSfW7hMmGlqaLfQEH1ryHefIIpkgKJa6WgVxA=
=f+tz
-----END PGP PUBLIC KEY BLOCK-----

Some files were not shown because too many files have changed in this diff Show More