mirror of
https://github.com/archr-linux/Arch-R.git
synced 2026-03-31 14:41:55 -07:00
1303 lines
50 KiB
Bash
Executable File
1303 lines
50 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
#==============================================================================
|
|
# Arch R - Root Filesystem Build Script
|
|
#==============================================================================
|
|
# Creates a minimal Arch Linux ARM rootfs optimized for R36S gaming
|
|
#==============================================================================
|
|
|
|
set -e
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
# Colors
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m'
|
|
|
|
log() { echo -e "${GREEN}[$(date '+%H:%M:%S')]${NC} $1"; }
|
|
warn() { echo -e "${YELLOW}[$(date '+%H:%M:%S')] WARNING:${NC} $1"; }
|
|
error() { echo -e "${RED}[$(date '+%H:%M:%S')] ERROR:${NC} $1"; exit 1; }
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Configuration
|
|
#------------------------------------------------------------------------------
|
|
|
|
OUTPUT_DIR="$SCRIPT_DIR/output"
|
|
ROOTFS_DIR="$OUTPUT_DIR/rootfs"
|
|
CACHE_DIR="$SCRIPT_DIR/.cache"
|
|
|
|
# CRITICAL: Always clean up bind mounts on exit (success OR failure).
|
|
cleanup_mounts() {
|
|
echo "[ROOTFS] Cleaning up bind mounts..."
|
|
umount -l "$ROOTFS_DIR/run" 2>/dev/null || true
|
|
umount -l "$ROOTFS_DIR/sys" 2>/dev/null || true
|
|
umount -l "$ROOTFS_DIR/proc" 2>/dev/null || true
|
|
umount -l "$ROOTFS_DIR/dev/pts" 2>/dev/null || true
|
|
umount -l "$ROOTFS_DIR/dev" 2>/dev/null || true
|
|
}
|
|
trap cleanup_mounts EXIT
|
|
|
|
# Arch Linux ARM rootfs
|
|
ALARM_URL="http://os.archlinuxarm.org/os/ArchLinuxARM-aarch64-latest.tar.gz"
|
|
ALARM_TARBALL="$CACHE_DIR/ArchLinuxARM-aarch64-latest.tar.gz"
|
|
|
|
log "=== Arch R Rootfs Build ==="
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Root check
|
|
#------------------------------------------------------------------------------
|
|
if [ "$EUID" -ne 0 ]; then
|
|
error "This script must be run as root (for chroot and permissions)"
|
|
fi
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Step 1: Download Arch Linux ARM
|
|
#------------------------------------------------------------------------------
|
|
log ""
|
|
log "Step 1: Checking Arch Linux ARM tarball..."
|
|
|
|
mkdir -p "$CACHE_DIR"
|
|
|
|
if [ ! -f "$ALARM_TARBALL" ]; then
|
|
log " Downloading Arch Linux ARM..."
|
|
wget -O "$ALARM_TARBALL" "$ALARM_URL"
|
|
else
|
|
log " ✓ Using cached tarball"
|
|
fi
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Step 2: Extract Base System
|
|
#------------------------------------------------------------------------------
|
|
log ""
|
|
log "Step 2: Extracting base system..."
|
|
|
|
# Clean previous rootfs (unmount stale bind mounts first)
|
|
if [ -d "$ROOTFS_DIR" ]; then
|
|
warn "Removing existing rootfs..."
|
|
for mp in run sys proc dev/pts dev; do
|
|
mountpoint -q "$ROOTFS_DIR/$mp" 2>/dev/null && umount -l "$ROOTFS_DIR/$mp" 2>/dev/null || true
|
|
done
|
|
rm -rf "$ROOTFS_DIR"
|
|
fi
|
|
|
|
mkdir -p "$ROOTFS_DIR"
|
|
|
|
log " Extracting... (this may take a while)"
|
|
bsdtar -xpf "$ALARM_TARBALL" -C "$ROOTFS_DIR"
|
|
|
|
log " ✓ Base system extracted"
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Step 3: Setup for chroot
|
|
#------------------------------------------------------------------------------
|
|
log ""
|
|
log "Step 3: Setting up chroot environment..."
|
|
|
|
# Copy QEMU for ARM64 emulation
|
|
if [ -f "/usr/bin/qemu-aarch64-static" ]; then
|
|
cp /usr/bin/qemu-aarch64-static "$ROOTFS_DIR/usr/bin/"
|
|
log " ✓ QEMU static copied"
|
|
else
|
|
warn "qemu-aarch64-static not found, chroot may not work"
|
|
warn "Install with: sudo apt install qemu-user-static"
|
|
fi
|
|
|
|
# Bind mounts
|
|
mount --bind /dev "$ROOTFS_DIR/dev"
|
|
mount --bind /dev/pts "$ROOTFS_DIR/dev/pts"
|
|
mount --bind /proc "$ROOTFS_DIR/proc"
|
|
mount --bind /sys "$ROOTFS_DIR/sys"
|
|
mount --bind /run "$ROOTFS_DIR/run"
|
|
|
|
# DNS resolution
|
|
rm "$ROOTFS_DIR/etc/resolv.conf"
|
|
cp /etc/resolv.conf "$ROOTFS_DIR/etc/resolv.conf"
|
|
|
|
log " ✓ Chroot environment ready"
|
|
|
|
# Fix pacman for QEMU chroot environment
|
|
# - Disable CheckSpace (mount point detection fails in chroot)
|
|
sed -i 's/^CheckSpace/#CheckSpace/' "$ROOTFS_DIR/etc/pacman.conf"
|
|
log " ✓ Pacman CheckSpace disabled (QEMU chroot compatibility)"
|
|
|
|
# Add multiple fallback mirrors (default mirror often has stale/404 packages)
|
|
cat > "$ROOTFS_DIR/etc/pacman.d/mirrorlist" << 'MIRRORS_EOF'
|
|
# Arch Linux ARM mirrors - all official, Americas first
|
|
Server = http://fl.us.mirror.archlinuxarm.org/$arch/$repo
|
|
Server = http://nj.us.mirror.archlinuxarm.org/$arch/$repo
|
|
Server = http://ca.us.mirror.archlinuxarm.org/$arch/$repo
|
|
Server = http://eu.mirror.archlinuxarm.org/$arch/$repo
|
|
Server = http://de.mirror.archlinuxarm.org/$arch/$repo
|
|
Server = http://de4.mirror.archlinuxarm.org/$arch/$repo
|
|
Server = http://dk.mirror.archlinuxarm.org/$arch/$repo
|
|
Server = http://hu.mirror.archlinuxarm.org/$arch/$repo
|
|
Server = http://gr.mirror.archlinuxarm.org/$arch/$repo
|
|
Server = http://de3.mirror.archlinuxarm.org/$arch/$repo
|
|
Server = http://tw.mirror.archlinuxarm.org/$arch/$repo
|
|
Server = http://tw2.mirror.archlinuxarm.org/$arch/$repo
|
|
Server = http://mirror.archlinuxarm.org/$arch/$repo
|
|
MIRRORS_EOF
|
|
log " ✓ Multiple ALARM mirrors configured (fallback for 404s)"
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Step 4: Configure System
|
|
#------------------------------------------------------------------------------
|
|
log ""
|
|
log "Step 4: Configuring system..."
|
|
|
|
# Create setup script to run inside chroot
|
|
cat > "$ROOTFS_DIR/tmp/setup.sh" << 'SETUP_EOF'
|
|
#!/bin/bash
|
|
set -e
|
|
|
|
echo "=== Inside chroot ==="
|
|
|
|
# Disable pacman Landlock sandbox (fails in QEMU chroot)
|
|
# Shell function wraps all pacman calls with --disable-sandbox
|
|
pacman() { command pacman --disable-sandbox "$@"; }
|
|
echo " Pacman sandbox disabled (--disable-sandbox wrapper)"
|
|
|
|
# Initialize pacman keyring
|
|
pacman-key --init
|
|
pacman-key --populate archlinuxarm
|
|
|
|
# Remove stock ALARM kernel + mkinitcpio BEFORE upgrade
|
|
# We build our own kernel 6.6.89 — stock linux-aarch64 triggers mkinitcpio
|
|
# hooks during pacman -Syu, causing harmless but noisy warnings.
|
|
pacman -Rdd --noconfirm linux-aarch64 mkinitcpio mkinitcpio-busybox 2>/dev/null || true
|
|
|
|
# Full system upgrade — multiple mirrors configured for 404 fallback
|
|
pacman -Syu --noconfirm --disable-download-timeout
|
|
|
|
# Install essential packages
|
|
pacman -S --noconfirm --needed \
|
|
base \
|
|
linux-firmware \
|
|
networkmanager \
|
|
wpa_supplicant \
|
|
dhcpcd \
|
|
sudo \
|
|
nano \
|
|
htop \
|
|
wget \
|
|
usb_modeswitch \
|
|
dosfstools \
|
|
parted \
|
|
i2c-tools
|
|
|
|
# Audio
|
|
pacman -S --noconfirm --needed \
|
|
alsa-utils \
|
|
alsa-plugins
|
|
|
|
# Bluetooth
|
|
pacman -S --noconfirm --needed \
|
|
bluez \
|
|
bluez-utils
|
|
|
|
# Graphics & GPU runtime dependencies
|
|
# NOTE: Do NOT install 'mesa' here! build-mesa.sh builds Mesa 26 from source
|
|
# with -Dgles1=enabled -Dglvnd=false. Installing pacman's mesa first would
|
|
# add glvnd files that conflict with our custom build.
|
|
pacman -S --noconfirm --needed \
|
|
libdrm \
|
|
sdl2 \
|
|
sdl2_mixer \
|
|
sdl2_image \
|
|
sdl2_ttf
|
|
|
|
# Gaming stack dependencies
|
|
# NOTE: Do NOT install 'retroarch' here! build-retroarch.sh builds from source
|
|
# with KMS/DRM + EGL (no X11/Qt). ALARM's retroarch package uses X11/Qt,
|
|
# pulling in ~200MB of unnecessary X11 dependencies.
|
|
# Note: freeimage is not in ALARM repos — installed in build-emulationstation.sh
|
|
pacman -S --noconfirm --needed \
|
|
libretro-core-info \
|
|
freetype2 \
|
|
libglvnd \
|
|
mbedtls \
|
|
curl \
|
|
unzip \
|
|
p7zip \
|
|
evtest \
|
|
brightnessctl \
|
|
python-evdev
|
|
|
|
# LibRetro cores — all from ALARM pacman repos (native aarch64)
|
|
# The libretro buildbot has NO aarch64 Linux builds — ALARM is the only source.
|
|
# Install each individually (some may not exist in all mirror snapshots)
|
|
for core in \
|
|
libretro-snes9x \
|
|
libretro-gambatte \
|
|
libretro-mgba \
|
|
libretro-genesis-plus-gx \
|
|
libretro-pcsx-rearmed \
|
|
libretro-flycast \
|
|
libretro-beetle-pce-fast \
|
|
libretro-scummvm \
|
|
libretro-melonds \
|
|
libretro-nestopia \
|
|
libretro-picodrive \
|
|
libretro-mesen \
|
|
libretro-duckstation \
|
|
libretro-beetle-psx-hw \
|
|
libretro-mame2016 \
|
|
libretro-yabause \
|
|
libretro-sameboy \
|
|
libretro-beetle-supergrafx; do
|
|
pacman -S --noconfirm --needed "$core" 2>/dev/null \
|
|
&& echo " Installed: $core" \
|
|
|| echo " Not available: $core"
|
|
done
|
|
|
|
# Clean up any 0-byte core files (from previous failed downloads)
|
|
CORES_DIR="/usr/lib/libretro"
|
|
find "$CORES_DIR" -name "*_libretro.so" -size 0 -delete -print 2>/dev/null | \
|
|
while read f; do echo " Removed 0-byte: $(basename "$f")"; done
|
|
|
|
|
|
# Enable services
|
|
systemctl enable NetworkManager
|
|
|
|
# Disable unnecessary services for faster boot
|
|
systemctl disable systemd-timesyncd 2>/dev/null || true
|
|
systemctl disable dhcpcd 2>/dev/null || true
|
|
systemctl disable remote-fs.target 2>/dev/null || true
|
|
|
|
# Create gaming user 'archr'
|
|
if ! id archr &>/dev/null; then
|
|
useradd -m -G wheel,audio,video,render,input -s /bin/bash archr
|
|
echo "archr:archr" | chpasswd
|
|
fi
|
|
|
|
# Allow wheel group passwordless sudo
|
|
echo "%wheel ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/wheel-nopasswd
|
|
|
|
# Set hostname
|
|
echo "archr" > /etc/hostname
|
|
|
|
# Set locale
|
|
echo "en_US.UTF-8 UTF-8" > /etc/locale.gen
|
|
locale-gen
|
|
echo "LANG=en_US.UTF-8" > /etc/locale.conf
|
|
|
|
# Set timezone
|
|
ln -sf /usr/share/zoneinfo/America/Sao_Paulo /etc/localtime
|
|
|
|
# Performance tuning
|
|
cat > /etc/sysctl.d/99-archr.conf << 'SYSCTL_EOF'
|
|
# Arch R Performance Tuning
|
|
vm.swappiness=10
|
|
vm.dirty_ratio=20
|
|
vm.dirty_background_ratio=5
|
|
kernel.sched_latency_ns=1000000
|
|
kernel.sched_min_granularity_ns=500000
|
|
SYSCTL_EOF
|
|
|
|
# Memory manager — ZRAM swap with delayed start (15s after boot)
|
|
# Timer avoids blocking multi-user.target (ES is already running by then)
|
|
cat > /etc/systemd/system/archr-memory-manager.service << 'MEM_SVC'
|
|
[Unit]
|
|
Description=Arch R Memory Manager
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
RemainAfterExit=yes
|
|
ExecStart=/usr/local/bin/archr-memory-manager setup
|
|
ExecStop=/usr/local/bin/archr-memory-manager stop
|
|
ExecReload=/usr/local/bin/archr-memory-manager reload
|
|
MEM_SVC
|
|
|
|
cat > /etc/systemd/system/archr-memory-manager.timer << 'MEM_TIMER'
|
|
[Unit]
|
|
Description=Delayed Memory Manager Setup
|
|
|
|
[Timer]
|
|
OnBootSec=15s
|
|
Unit=archr-memory-manager.service
|
|
|
|
[Install]
|
|
WantedBy=timers.target
|
|
MEM_TIMER
|
|
|
|
systemctl enable archr-memory-manager.timer
|
|
|
|
# Create directories
|
|
mkdir -p /home/archr/.config/retroarch/cores
|
|
mkdir -p /home/archr/.config/retroarch/saves
|
|
mkdir -p /home/archr/.config/retroarch/states
|
|
mkdir -p /home/archr/.config/retroarch/screenshots
|
|
mkdir -p /roms
|
|
chown -R archr:archr /home/archr
|
|
|
|
# Fix RetroArch core info cache: allow archr to write cache file
|
|
# Without this, RetroArch logs "Failed to write core info cache file" every launch
|
|
mkdir -p /usr/share/libretro/info
|
|
chmod 777 /usr/share/libretro/info
|
|
|
|
# Ensure mbedtls .so symlinks exist (RetroArch links against libmbedtls.so.21)
|
|
# Package updates may change the .so version; create compat symlinks
|
|
for lib in libmbedtls libmbedcrypto libmbedx509; do
|
|
LATEST=$(ls /usr/lib/${lib}.so.* 2>/dev/null | sort -V | tail -1)
|
|
if [ -n "$LATEST" ]; then
|
|
# Create .so.21 symlink if it doesn't exist (RetroArch v1.22.2 expects it)
|
|
[ ! -e /usr/lib/${lib}.so.21 ] && ln -sf "$(basename "$LATEST")" /usr/lib/${lib}.so.21
|
|
fi
|
|
done
|
|
|
|
# Add ROMS partition to fstab (firstboot creates the partition)
|
|
# Note: build-image.sh overwrites fstab with final version, this is a fallback
|
|
if ! grep -q '/roms' /etc/fstab; then
|
|
echo '# ROMS partition (created by firstboot)' >> /etc/fstab
|
|
echo 'LABEL=ROMS /roms vfat defaults,utf8,noatime,uid=1001,gid=1001,nofail,x-systemd.device-timeout=10s 0 0' >> /etc/fstab
|
|
fi
|
|
|
|
# Firstboot service
|
|
cat > /etc/systemd/system/firstboot.service << 'FB_EOF'
|
|
[Unit]
|
|
Description=Arch R First Boot Setup
|
|
After=local-fs.target
|
|
Before=emulationstation.service
|
|
ConditionPathExists=!/var/lib/archr/.first-boot-done
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
ExecStart=/usr/local/bin/first-boot.sh
|
|
RemainAfterExit=yes
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
FB_EOF
|
|
|
|
systemctl enable firstboot
|
|
|
|
# Variant sync: copies variant marker from BOOT → /etc/archr/variant
|
|
# Only runs if /etc/archr/variant doesn't exist yet (no-panel images flashed by Flasher)
|
|
cat > /etc/systemd/system/archr-variant-sync.service << 'VARSYNC_EOF'
|
|
[Unit]
|
|
Description=Sync variant from BOOT partition
|
|
After=local-fs.target
|
|
RequiresMountsFor=/boot
|
|
ConditionPathExists=!/etc/archr/variant
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
ExecStart=/bin/sh -c 'test -f /boot/variant && mkdir -p /etc/archr && cp /boot/variant /etc/archr/variant'
|
|
RemainAfterExit=yes
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
VARSYNC_EOF
|
|
|
|
systemctl enable archr-variant-sync
|
|
|
|
# EmulationStation launch — PRIMARY: systemd service (created by build-emulationstation.sh)
|
|
# FALLBACK: autologin + .bash_profile (if ES service not installed)
|
|
# The emulationstation.service Conflicts with getty@tty1, so only one runs.
|
|
# Autologin override below is kept as fallback for manual/debug scenarios.
|
|
|
|
# Boot-time setup service (runs as root before ES: GPU + governors + DRM + runtime dir)
|
|
# DefaultDependencies=no + After=udevd → starts ASAP after devices appear
|
|
cat > /etc/systemd/system/archr-boot-setup.service << 'BOOTSETUP_SVC'
|
|
[Unit]
|
|
Description=Arch R Boot Setup
|
|
DefaultDependencies=no
|
|
After=systemd-udevd.service
|
|
Before=emulationstation.service
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
RemainAfterExit=yes
|
|
ExecStart=/bin/bash -c 'modprobe panfrost 2>/dev/null; mkdir -p /run/user/1001 && chown 1001:1001 /run/user/1001; echo performance > /sys/devices/system/cpu/cpufreq/policy0/scaling_governor 2>/dev/null; echo performance > /sys/devices/platform/ff400000.gpu/devfreq/ff400000.gpu/governor 2>/dev/null'
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
BOOTSETUP_SVC
|
|
|
|
systemctl enable archr-boot-setup
|
|
|
|
# .bash_profile for archr — launches EmulationStation on tty1 only
|
|
# exec replaces the shell process (saves memory, cleaner process tree)
|
|
cat > /home/archr/.bash_profile << 'PROFILE_EOF'
|
|
# Arch R: Auto-launch EmulationStation on tty1
|
|
if [ "$(tty)" = "/dev/tty1" ]; then
|
|
echo "$(cut -d' ' -f1 /proc/uptime) bash_profile_start" >> /tmp/boot-timeline.txt
|
|
exec /usr/bin/emulationstation/emulationstation.sh
|
|
fi
|
|
PROFILE_EOF
|
|
chown archr:archr /home/archr/.bash_profile
|
|
|
|
# Boot splash: handled by initramfs (archr-init with embedded BMP).
|
|
# Initramfs displays splash at ~0.7s after kernel start, before systemd.
|
|
# No systemd service needed — splash persists until ES takes DRM master.
|
|
systemctl disable splash 2>/dev/null || true
|
|
systemctl disable archr-splash 2>/dev/null || true
|
|
rm -f /etc/systemd/system/splash.service /etc/systemd/system/archr-splash.service
|
|
|
|
# Battery LED warning service
|
|
cat > /etc/systemd/system/battery-led.service << 'BATT_EOF'
|
|
[Unit]
|
|
Description=Arch R Battery LED Warning
|
|
After=local-fs.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
ExecStart=/usr/bin/python3 /usr/local/bin/batt_life_warning.py
|
|
Restart=on-failure
|
|
RestartSec=30
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
BATT_EOF
|
|
|
|
systemctl enable battery-led
|
|
|
|
# Hotkey daemon (volume/brightness)
|
|
cat > /etc/systemd/system/archr-hotkeys.service << 'HK_EOF'
|
|
[Unit]
|
|
Description=Arch R Hotkey Daemon (volume/brightness)
|
|
After=local-fs.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
ExecStart=/usr/bin/python3 /usr/local/bin/archr-hotkeys.py
|
|
Restart=on-failure
|
|
RestartSec=5
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
HK_EOF
|
|
|
|
systemctl enable archr-hotkeys
|
|
|
|
# Boot timing capture — Type=simple + background fork so it doesn't block boot completion!
|
|
# Previous Type=oneshot was self-defeating: sleep 3s → systemd-analyze says "not yet finished"
|
|
# because the boot-timing service ITSELF was still a pending job.
|
|
cat > /etc/systemd/system/boot-timing.service << 'TIMING_EOF'
|
|
[Unit]
|
|
Description=Capture boot timing
|
|
After=emulationstation.service
|
|
After=multi-user.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
ExecStart=/bin/bash -c '\
|
|
sleep 15; \
|
|
{ \
|
|
echo "=== Boot Timing ==="; \
|
|
systemd-analyze 2>&1 || true; \
|
|
echo ""; \
|
|
echo "=== Blame (top 20) ==="; \
|
|
systemd-analyze blame --no-pager 2>&1 | head -20 || true; \
|
|
echo ""; \
|
|
echo "=== Critical Chain ==="; \
|
|
systemd-analyze critical-chain --no-pager 2>&1 || true; \
|
|
echo ""; \
|
|
echo "=== ES Timeline ==="; \
|
|
cat /home/archr/es-timeline.txt 2>/dev/null || echo "no timeline"; \
|
|
echo ""; \
|
|
echo "=== ES Debug (profiling) ==="; \
|
|
grep "\\[BOOT" /home/archr/es-debug.log 2>/dev/null || echo "no profiling"; \
|
|
echo ""; \
|
|
echo "=== boot_setup marker ==="; \
|
|
cat /tmp/boot-timeline.txt 2>/dev/null || echo "no marker"; \
|
|
} > /boot/boot-timing.txt 2>&1; \
|
|
chmod 644 /boot/boot-timing.txt'
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
TIMING_EOF
|
|
|
|
systemctl enable boot-timing
|
|
|
|
# Debug dump: comprehensive system info to /boot/debug.log (readable from PC)
|
|
cat > /etc/systemd/system/archr-debug-dump.service << 'DEBUG_EOF'
|
|
[Unit]
|
|
Description=Arch R Debug Dump
|
|
After=emulationstation.service
|
|
After=multi-user.target
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
ExecStart=/bin/bash -c '\
|
|
sleep 20; \
|
|
{ \
|
|
echo "=== Arch R Debug Dump ($(date)) ==="; \
|
|
echo ""; \
|
|
echo "--- Kernel ---"; \
|
|
uname -a; \
|
|
echo ""; \
|
|
echo "--- Input Devices ---"; \
|
|
for d in /dev/input/event*; do \
|
|
name=$(cat /sys/class/input/$(basename $d)/device/name 2>/dev/null); \
|
|
echo " $d: $name"; \
|
|
done; \
|
|
echo ""; \
|
|
echo "--- DRM ---"; \
|
|
for c in /sys/class/drm/card*/status; do \
|
|
echo " $c: $(cat $c 2>/dev/null)"; \
|
|
done; \
|
|
cat /sys/class/drm/card*/modes 2>/dev/null | head -5; \
|
|
echo ""; \
|
|
echo "--- Backlight ---"; \
|
|
for bl in /sys/class/backlight/*/; do \
|
|
echo " $(basename $bl): cur=$(cat ${bl}brightness 2>/dev/null) max=$(cat ${bl}max_brightness 2>/dev/null)"; \
|
|
done; \
|
|
echo ""; \
|
|
echo "--- ALSA Controls ---"; \
|
|
amixer scontrols 2>/dev/null || echo " (no ALSA)"; \
|
|
echo ""; \
|
|
echo "--- CPU/GPU ---"; \
|
|
echo " CPU: $(cat /sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq 2>/dev/null)kHz ($(cat /sys/devices/system/cpu/cpufreq/policy0/scaling_governor 2>/dev/null))"; \
|
|
echo " GPU: $(cat /sys/devices/platform/ff400000.gpu/devfreq/ff400000.gpu/cur_freq 2>/dev/null)Hz"; \
|
|
echo " Temp: $(cat /sys/class/thermal/thermal_zone0/temp 2>/dev/null)"; \
|
|
echo ""; \
|
|
echo "--- Memory ---"; \
|
|
free -h; \
|
|
echo ""; \
|
|
echo "--- Loaded Modules ---"; \
|
|
lsmod 2>/dev/null | head -30; \
|
|
echo ""; \
|
|
echo "--- Failed Services ---"; \
|
|
systemctl --failed --no-pager 2>/dev/null; \
|
|
echo ""; \
|
|
echo "--- dmesg errors ---"; \
|
|
dmesg -l err,warn 2>/dev/null | tail -30; \
|
|
} > /boot/debug.log 2>&1; \
|
|
chmod 644 /boot/debug.log'
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
DEBUG_EOF
|
|
|
|
systemctl enable archr-debug-dump
|
|
|
|
# USB auto-mount service (triggered by udev on device insertion)
|
|
cat > /etc/systemd/system/archr-automount.service << 'AUTOMOUNT_SVC'
|
|
[Unit]
|
|
Description=Arch R USB Auto-Mount
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
ExecStart=/usr/local/bin/archr-automount mount
|
|
AUTOMOUNT_SVC
|
|
|
|
# Save config on shutdown (backs up ALSA state, brightness, WiFi to /boot)
|
|
cat > /etc/systemd/system/archr-save-config.service << 'SAVECONF_SVC'
|
|
[Unit]
|
|
Description=Arch R Save Config
|
|
DefaultDependencies=no
|
|
Before=shutdown.target
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
ExecStart=/usr/local/bin/archr-save-config backup
|
|
|
|
[Install]
|
|
WantedBy=shutdown.target
|
|
SAVECONF_SVC
|
|
|
|
systemctl enable archr-save-config
|
|
|
|
# Bluetooth auto-pairing agent (runs alongside bluetoothd)
|
|
cat > /etc/systemd/system/archr-bluetooth-agent.service << 'BTAGENT_SVC'
|
|
[Unit]
|
|
Description=Arch R Bluetooth Agent
|
|
After=bluetooth.service
|
|
PartOf=bluetooth.service
|
|
|
|
[Service]
|
|
Type=simple
|
|
ExecStart=/usr/local/bin/archr-bluetooth-agent
|
|
Restart=on-failure
|
|
RestartSec=10
|
|
|
|
[Install]
|
|
WantedBy=bluetooth.target
|
|
BTAGENT_SVC
|
|
|
|
systemctl enable archr-bluetooth-agent
|
|
|
|
# Sleep configuration (suspend disabled by default, user enables via archr-suspend-mode)
|
|
mkdir -p /etc/systemd/sleep.conf.d
|
|
cat > /etc/systemd/sleep.conf.d/archr.conf << 'SLEEP_CONF'
|
|
[Sleep]
|
|
AllowSuspend=yes
|
|
SuspendState=mem
|
|
AllowHibernation=no
|
|
SLEEP_CONF
|
|
|
|
# Modules blacklist for sleep (unloaded before suspend by archr-sleep hook)
|
|
mkdir -p /etc/archr
|
|
install -m 644 /tmp/modules.bad /etc/archr/modules.bad 2>/dev/null || true
|
|
|
|
# Delete stock ALARM initramfs and kernel (we use our own kernel + initramfs)
|
|
# Our initramfs.img is built by build-image.sh and placed on BOOT partition
|
|
rm -f /boot/initramfs-linux.img /boot/Image /boot/Image.gz 2>/dev/null
|
|
|
|
# Clean kernel modules: remove media tuners (154 modules, 13MB — useless for gaming)
|
|
rm -rf /lib/modules/*/kernel/drivers/media 2>/dev/null
|
|
rm -rf /lib/modules/*/kernel/drivers/misc 2>/dev/null
|
|
depmod -a 2>/dev/null || true
|
|
|
|
# Pre-seed random seed (even though random-seed service is masked)
|
|
mkdir -p /var/lib/systemd
|
|
dd if=/dev/urandom of=/var/lib/systemd/random-seed bs=512 count=1 2>/dev/null
|
|
chmod 600 /var/lib/systemd/random-seed
|
|
|
|
# Sudoers for perfmax/perfnorm/pmic-poweroff (allow archr to run without password)
|
|
echo "archr ALL=(ALL) NOPASSWD: /usr/local/bin/perfmax, /usr/local/bin/perfnorm, /usr/local/bin/pmic-poweroff, /usr/local/bin/input-merge, /usr/local/bin/archr-gptokeyb, /usr/local/bin/archr-factory-reset, /usr/local/bin/archr-suspend-mode, /usr/local/bin/archr-usbgadget, /usr/bin/kill, /usr/bin/ln, /usr/bin/dmesg, /usr/bin/chvt, /usr/bin/cp, /usr/bin/chmod, /usr/bin/tee, /usr/bin/systemctl, /bin/bash" > /etc/sudoers.d/archr-perf
|
|
chmod 440 /etc/sudoers.d/archr-perf
|
|
|
|
# Allow archr to use negative nice values (needed for nice -n -19 in ES launch commands)
|
|
echo "archr - nice -20" >> /etc/security/limits.conf
|
|
|
|
# Distro version info
|
|
cat > /etc/archr-release << 'VER_EOF'
|
|
NAME="Arch R"
|
|
VERSION="1.0"
|
|
ID=archr
|
|
ID_LIKE=arch
|
|
BUILD_DATE="$(date +%Y-%m-%d)"
|
|
VARIANT="R36S"
|
|
VER_EOF
|
|
|
|
# Auto-login on tty1 — fast ES launch path
|
|
# --skip-login: bypass /bin/login entirely (no PAM, no visible login prompt)
|
|
# --noissue/--noclear: skip /etc/issue, keep splash on screen
|
|
# Type=simple: base getty@.service has Type=idle (waits for ALL jobs). Override to simple.
|
|
# ExecStartPre: blank VT text layer (black-on-black) so no text flickers on screen
|
|
mkdir -p /etc/systemd/system/getty@tty1.service.d
|
|
cat > /etc/systemd/system/getty@tty1.service.d/autologin.conf << 'AL_EOF'
|
|
[Service]
|
|
ExecStartPre=-/bin/bash -c 'printf "\033[2J\033[H\033[?25l\033[30;40m" > /dev/tty1 2>/dev/null'
|
|
ExecStart=
|
|
ExecStart=-/sbin/agetty --autologin archr --skip-login --noclear --noissue %I $TERM
|
|
Type=simple
|
|
StandardInput=tty
|
|
StandardOutput=tty
|
|
TTYVTDisallocate=no
|
|
AL_EOF
|
|
|
|
# Journald size limit (save memory)
|
|
mkdir -p /etc/systemd/journald.conf.d
|
|
cat > /etc/systemd/journald.conf.d/size.conf << 'JD_EOF'
|
|
[Journal]
|
|
SystemMaxUse=16M
|
|
RuntimeMaxUse=16M
|
|
JD_EOF
|
|
|
|
# Suppress login messages (silent boot)
|
|
touch /home/archr/.hushlogin
|
|
chown archr:archr /home/archr/.hushlogin
|
|
|
|
# logind: minimize overhead for single-user gaming device
|
|
mkdir -p /etc/systemd/logind.conf.d
|
|
cat > /etc/systemd/logind.conf.d/fast.conf << 'LOGIND_EOF'
|
|
[Login]
|
|
NAutoVTs=1
|
|
ReserveVT=0
|
|
HandlePowerKey=suspend
|
|
HandleSuspendKey=suspend
|
|
HandleHibernateKey=ignore
|
|
HandleLidSwitch=ignore
|
|
KillUserProcesses=no
|
|
UserStopDelaySec=0
|
|
LOGIND_EOF
|
|
|
|
# user@.service: defer systemd user session (off critical path)
|
|
mkdir -p /etc/systemd/system/user@.service.d
|
|
cat > /etc/systemd/system/user@.service.d/fast-boot.conf << 'USER_EOF'
|
|
[Unit]
|
|
After=local-fs.target
|
|
USER_EOF
|
|
|
|
# Pre-create build-time configs (saves ~1s of shell fork/exec at ES startup)
|
|
# .drirc — suppresses Mesa warnings about missing per-app config
|
|
echo '<?xml version="1.0"?><driconf/>' > /home/archr/.drirc
|
|
chown archr:archr /home/archr/.drirc
|
|
# Mesa shader cache dir
|
|
mkdir -p /home/archr/.cache/mesa_shader_cache
|
|
chown -R archr:archr /home/archr/.cache
|
|
|
|
#------------------------------------------------------------------------------
|
|
# System Optimization
|
|
#------------------------------------------------------------------------------
|
|
echo "=== System Optimization ==="
|
|
|
|
# Note: tmpfs entries for /tmp and /var/log are set in build-image.sh's fstab
|
|
# (build-image.sh creates a fresh fstab that overrides what's in the rootfs)
|
|
|
|
# ======================================================================
|
|
# BOOT SPEED OPTIMIZATION
|
|
# ArkOS/dArkOS boot in ~3s. Full systemd with default ALARM services
|
|
# adds 15-30s of unnecessary overhead. Disable/mask everything not needed.
|
|
# ======================================================================
|
|
|
|
# Disable services that are not needed on this device
|
|
systemctl disable lvm2-monitor 2>/dev/null || true
|
|
systemctl mask lvm2-lvmpolld.service lvm2-lvmpolld.socket 2>/dev/null || true
|
|
|
|
# Network wait services — BIGGEST boot blocker! Waits for network that
|
|
# doesn't exist (no WiFi configured) = 5-30s timeout on EACH boot.
|
|
systemctl disable NetworkManager-wait-online.service 2>/dev/null || true
|
|
systemctl disable systemd-networkd-wait-online.service 2>/dev/null || true
|
|
|
|
# systemd-networkd conflicts with NetworkManager — only need one.
|
|
# NM is used for WiFi config UI. Networkd is server-oriented.
|
|
systemctl disable systemd-networkd.service 2>/dev/null || true
|
|
systemctl disable systemd-network-generator.service 2>/dev/null || true
|
|
|
|
# systemd-resolved — DNS resolver, overkill for embedded gaming device.
|
|
# NM handles DNS directly. Use /etc/resolv.conf as fallback.
|
|
systemctl disable systemd-resolved.service 2>/dev/null || true
|
|
rm -f /etc/resolv.conf
|
|
echo -e "nameserver 8.8.8.8\nnameserver 1.1.1.1" > /etc/resolv.conf
|
|
|
|
# sshd — not useful until WiFi is configured. Can enable later.
|
|
systemctl disable sshd.service 2>/dev/null || true
|
|
|
|
# Mask ConditionNeedsUpdate services — caches are pre-built during
|
|
# rootfs creation. Without masking, any file change in /usr/ (deploying
|
|
# new cores, scripts, etc.) makes /usr newer than the update markers,
|
|
# triggering full ldconfig/hwdb/catalog rebuild on EVERY boot.
|
|
systemctl mask ldconfig.service 2>/dev/null || true
|
|
systemctl mask systemd-hwdb-update.service 2>/dev/null || true
|
|
systemctl mask systemd-journal-catalog-update.service 2>/dev/null || true
|
|
systemctl mask systemd-sysusers.service 2>/dev/null || true
|
|
|
|
# Mask slow boot services (saves ~12s on ARM)
|
|
# systemd-random-seed: blocks 6s+ waiting for entropy (no HW RNG on RK3326)
|
|
systemctl mask systemd-random-seed.service 2>/dev/null || true
|
|
# Debug/tracing/config/fuse mounts: ~3s total, not needed for gaming
|
|
systemctl mask sys-kernel-debug.mount sys-kernel-tracing.mount \
|
|
sys-kernel-config.mount sys-fs-fuse-connections.mount 2>/dev/null || true
|
|
# Journal flush, vconsole, backlight, hostnamed: ~1.6s total
|
|
systemctl mask systemd-journal-flush.service systemd-vconsole-setup.service \
|
|
"systemd-backlight@backlight:backlight.service" \
|
|
systemd-hostnamed.service 2>/dev/null || true
|
|
# utmp, modules-load, binfmt, machine-id, pstore, sysext, time-sync, resolved, networkd
|
|
systemctl mask systemd-update-utmp.service systemd-update-utmp-runlevel.service \
|
|
systemd-modules-load.service systemd-binfmt.service \
|
|
systemd-machine-id-commit.service systemd-pstore.service \
|
|
systemd-sysext.service systemd-confext.service \
|
|
systemd-time-wait-sync.service systemd-timesyncd.service \
|
|
systemd-networkd.service systemd-resolved.service \
|
|
audit-rules.service auditd.service lastlog2-import.service \
|
|
mkinitcpio-generate-shutdown-ramfs.service \
|
|
alsa-restore.service 2>/dev/null || true
|
|
# alsa-restore: ES handles ALSA init (amixer sset), alsa-restore just loads asound.state
|
|
|
|
# Mask sockets not needed on R36S (saves socket activation overhead)
|
|
for sock in \
|
|
polkit-agent-helper.socket systemd-factory-reset.socket \
|
|
systemd-mute-console.socket systemd-bootctl.socket \
|
|
systemd-creds.socket systemd-repart.socket \
|
|
systemd-importd.socket systemd-machined.socket \
|
|
systemd-hostnamed.socket \
|
|
systemd-rfkill.socket dm-event.socket \
|
|
systemd-coredump.socket systemd-sysext.socket \
|
|
systemd-ask-password-wall.socket \
|
|
dirmngr@etc-pacman.d-gnupg.socket \
|
|
keyboxd@etc-pacman.d-gnupg.socket \
|
|
gpg-agent@etc-pacman.d-gnupg.socket \
|
|
gpg-agent-ssh@etc-pacman.d-gnupg.socket \
|
|
gpg-agent-browser@etc-pacman.d-gnupg.socket \
|
|
gpg-agent-extra@etc-pacman.d-gnupg.socket; do
|
|
systemctl mask "$sock" 2>/dev/null || true
|
|
done
|
|
|
|
# Disable NetworkManager from boot entirely — it adds 2-6s waiting for non-existent WiFi.
|
|
# User enables it manually when they want WiFi: systemctl enable --now NetworkManager
|
|
systemctl disable NetworkManager.service 2>/dev/null || true
|
|
|
|
# Mask udev rules not applicable to R36S (reduces udev coldplug time)
|
|
mkdir -p /etc/udev/rules.d
|
|
for rule in 60-cdrom_id 60-dmi-id 60-fido-id 60-infiniband \
|
|
60-persistent-storage-mtd 60-persistent-storage-tape 60-persistent-v4l \
|
|
60-sensor 60-serial 60-tpm-udev 64-btrfs 65-libwacom 70-camera \
|
|
70-infrared 70-touchpad 75-probe_mtd 81-net-bridge 81-net-dhcp \
|
|
82-net-auto-link-local 90-image-dissect 90-iocost 90-vconsole \
|
|
96-e2scrub 50-mali 10-dm 13-dm-disk 95-dm-notify; do
|
|
ln -sf /dev/null "/etc/udev/rules.d/${rule}.rules"
|
|
done
|
|
|
|
# Allow input group to use /dev/uinput (needed by archr-gptokeyb and input-merge)
|
|
echo 'KERNEL=="uinput", GROUP="input", MODE="0660"' > /etc/udev/rules.d/99-archr-uinput.rules
|
|
|
|
# Pre-build caches NOW (inside chroot) so masks are safe
|
|
ldconfig 2>/dev/null || true
|
|
journalctl --update-catalog 2>/dev/null || true
|
|
systemd-hwdb update 2>/dev/null || true
|
|
# Pre-seed random entropy
|
|
dd if=/dev/urandom of=/var/lib/systemd/random-seed bs=512 count=1 2>/dev/null || true
|
|
|
|
# Update markers — must be NEWER than /usr to prevent NeedsUpdate triggers
|
|
touch /etc/.updated /var/.updated
|
|
|
|
# Disable Mali blob from ld.so.conf — use Mesa Panfrost instead
|
|
# Mali's old libgbm.so (2020) is incompatible with modern SDL3/sdl2-compat
|
|
# Mali libs stay in /usr/lib/mali-egl/ for manual use if needed
|
|
if [ -f /etc/ld.so.conf.d/mali.conf ]; then
|
|
mv /etc/ld.so.conf.d/mali.conf /etc/ld.so.conf.d/mali.conf.disabled
|
|
ldconfig
|
|
fi
|
|
|
|
# Suppress ALL kernel messages on console (0 = emergency only)
|
|
echo 'kernel.printk = 0 0 0 0' >> /etc/sysctl.d/99-archr.conf
|
|
|
|
# Fast shutdown: 2s timeout instead of default 90s
|
|
# On a handheld, no service needs 90s to stop gracefully
|
|
mkdir -p /etc/systemd/system.conf.d
|
|
cat > /etc/systemd/system.conf.d/quick-shutdown.conf << 'SHUTDOWN_EOF'
|
|
[Manager]
|
|
DefaultTimeoutStopSec=2s
|
|
DefaultTimeoutAbortSec=2s
|
|
SHUTDOWN_EOF
|
|
|
|
# Faster TTY login (skip issue/motd)
|
|
echo "" > /etc/issue
|
|
echo "" > /etc/motd
|
|
|
|
# ALSA config for RK3326 (rk817 codec)
|
|
# Simple plug → hw:0,0 — no dmix needed (SDL_mixer handles mixing internally)
|
|
# dmix was causing issues with SDL_mixer; dArkOS also removes dmix for game launch
|
|
cat > /etc/asound.conf << 'ALSA_EOF'
|
|
# Arch R ALSA configuration for RK3326 (rk817 codec)
|
|
# Simple plug → hw:0,0 — no dmix needed (SDL_mixer handles mixing internally)
|
|
pcm.!default {
|
|
type plug
|
|
slave.pcm "hw:0,0"
|
|
}
|
|
ctl.!default {
|
|
type hw
|
|
card 0
|
|
}
|
|
ALSA_EOF
|
|
|
|
# Set default audio levels (rk817 BSP codec)
|
|
# ALSA simple mixer maps raw "DAC Playback Volume" to simple name "DAC"
|
|
# "Master" does NOT exist on rk817 — must use "DAC"
|
|
amixer -c 0 sset 'Playback Path' SPK 2>/dev/null || true
|
|
amixer -c 0 sset 'DAC' 80% 2>/dev/null || true
|
|
|
|
# Disable coredumps (save space)
|
|
echo 'kernel.core_pattern=|/bin/false' >> /etc/sysctl.d/99-archr.conf
|
|
mkdir -p /etc/systemd/coredump.conf.d
|
|
cat > /etc/systemd/coredump.conf.d/disable.conf << 'CORE_EOF'
|
|
[Coredump]
|
|
Storage=none
|
|
ProcessSizeMax=0
|
|
CORE_EOF
|
|
|
|
# Network defaults (WiFi powersave off for lower latency)
|
|
mkdir -p /etc/NetworkManager/conf.d
|
|
cat > /etc/NetworkManager/conf.d/archr.conf << 'NM_EOF'
|
|
[connection]
|
|
wifi.powersave=2
|
|
NM_EOF
|
|
|
|
# Clean package cache
|
|
pacman -Scc --noconfirm
|
|
|
|
# ======================================================================
|
|
# BLOAT REMOVAL — "Arch R deve ser leve como uma pluma"
|
|
# Gaming device needs runtime libs ONLY. Remove everything else.
|
|
# ======================================================================
|
|
|
|
echo "=== Bloat Removal ==="
|
|
|
|
# --- Firmware: keep only ARM + BT/WiFi USB dongles (782MB → 24MB) ---
|
|
FW="/usr/lib/firmware"
|
|
for dir in intel nvidia amdgpu ath11k ath12k ath10k i915 rtw89 \
|
|
mediatek dpaa2 ti-connectivity cypress cirrus radeon cxgb4 xe \
|
|
bnx2x ueagle-atm rsi nxp amlogic cnm ath6k ath9k_htc \
|
|
HP LENOVO dell vxge atmel ar3k slicoss moxa amd amphion \
|
|
amdnpu amdtee airoha meson aeonsemi go7007 bnx2 cxgb3 acenic \
|
|
advansys edgeport dabusb ene-ub6250 tigon sb16 yam ttusb-budget \
|
|
korg kaweth ess vicam dsp56k cpia2 av7110 adaptec sxg tehuti \
|
|
myricom matrox cavium inside-secure ixp4xx wfx sdca powervr \
|
|
isci e100 r128 ositech microchip imx keyspan keyspan_pda sun \
|
|
ti-keystone cadence; do
|
|
rm -rf "$FW/$dir" 2>/dev/null
|
|
done
|
|
# Remove individual firmware files for non-ARM hardware
|
|
find "$FW" -maxdepth 1 \( -name "*.bin" -o -name "*.fw" \) -size 0 -delete 2>/dev/null
|
|
rm -f "$FW"/iwlwifi-* "$FW"/phanfw.bin "$FW"/myri10ge_*.dat 2>/dev/null
|
|
rm -f "$FW"/hfi1_*.fw "$FW"/wil6210* "$FW"/s5p-mfc* "$FW"/wsm_22.bin 2>/dev/null
|
|
rm -f "$FW"/sms1xxx-*.fw "$FW"/dvb-*.fw "$FW"/v4l-*.fw 2>/dev/null
|
|
rm -f "$FW"/agere_*.bin "$FW"/isdbt_*.inp "$FW"/dvb_*.inp "$FW"/cmmb_*.inp "$FW"/tdmb_*.inp 2>/dev/null
|
|
rm -f "$FW"/lbtf_usb.bin "$FW"/ar5523.bin "$FW"/ar9170-*.fw "$FW"/ar7010*.fw 2>/dev/null
|
|
rm -f "$FW"/htc_*.fw "$FW"/f2255usb.bin "$FW"/tlg2300_firmware.bin 2>/dev/null
|
|
rm -f "$FW"/rcar_gen4_pcie.bin "$FW"/lt9611uxc_fw.bin "$FW"/tsse_firmware.bin 2>/dev/null
|
|
rm -f "$FW"/ctefx.bin "$FW"/ctspeq.bin "$FW"/ath3k-1.fw "$FW"/carl9170-*.fw 2>/dev/null
|
|
rm -f "$FW"/qat_*.bin "$FW"/r8a779x_*.dlmem "$FW"/usbdux*.bin "$FW"/whiteheat*.fw 2>/dev/null
|
|
rm -f "$FW"/TAS2XXX*.bin "$FW"/TXNW*.bin "$FW"/TIAS*.bin "$FW"/INT8866*.bin 2>/dev/null
|
|
echo " Firmware cleaned (kept: rockchip, brcm, rtl_bt, qca, rtw88, arm)"
|
|
|
|
# --- Headers: NEVER needed at runtime (420MB) ---
|
|
rm -rf /usr/include && mkdir -p /usr/include
|
|
echo " Removed /usr/include"
|
|
|
|
# --- Documentation (150MB + 75MB + 11MB) ---
|
|
rm -rf /usr/share/doc /usr/share/man /usr/share/info /usr/share/gtk-doc
|
|
mkdir -p /usr/share/doc
|
|
echo " Removed docs/man/info"
|
|
|
|
# --- Locales: keep only en_US, pt_BR (213MB → 9MB) ---
|
|
cd /usr/share/locale
|
|
for d in */; do
|
|
d="${d%/}"
|
|
case "$d" in en|en_US|pt|pt_BR|locale.alias) continue ;; *) rm -rf "$d" ;; esac
|
|
done
|
|
# i18n: keep only needed locale definitions
|
|
cd /usr/share/i18n/locales 2>/dev/null
|
|
for f in *; do
|
|
case "$f" in en_US|en_GB|pt_BR|POSIX|i18n|iso14651_t1|iso14651_t1_common|translit_*) continue ;; *) rm -f "$f" ;; esac
|
|
done
|
|
echo " Locales cleaned (kept: en_US, pt_BR)"
|
|
|
|
# --- Build tools: compilers, linkers, static libs ---
|
|
rm -rf /usr/lib/gcc /usr/lib/clang
|
|
rm -f /usr/lib/libclang-cpp.so* /usr/lib/libclang.so*
|
|
rm -f /usr/lib/libgo.so* /usr/lib/libgphobos.so* /usr/lib/libgdruntime.so*
|
|
rm -f /usr/lib/libgfortran.so*
|
|
rm -f /usr/lib/libasan.so* /usr/lib/libtsan.so* /usr/lib/libubsan.so* /usr/lib/liblsan.so*
|
|
rm -f /usr/lib/libteflon.so*
|
|
find /usr/lib -name "*.a" -delete 2>/dev/null
|
|
echo " Removed compilers, static libs, sanitizers"
|
|
|
|
# --- Unused libraries ---
|
|
rm -rf /usr/lib/mali-egl # Mali proprietary (using Panfrost)
|
|
rm -rf /usr/lib/guile /usr/lib/glycin-loaders /usr/lib/vlc
|
|
rm -rf /usr/lib/qt6 /usr/lib/qt /usr/lib/cmake /usr/lib/git-core
|
|
rm -f /usr/lib/libguile* /usr/lib/libQt5*.so* /usr/lib/libQt6*.so*
|
|
# VLC: remove full library but KEEP libvlc.so.5 stub — ES links against it at load time!
|
|
# ES handles libvlc_new() returning NULL gracefully (just skips video backgrounds)
|
|
rm -f /usr/lib/libvlccore*.so*
|
|
# Replace full libvlc with a minimal stub (no-op functions, ~68KB vs 15MB+)
|
|
# The stub is cross-compiled and stored in the project
|
|
if [ -f /tmp/libvlc-stub.so ]; then
|
|
cp /tmp/libvlc-stub.so /usr/lib/libvlc.so.5.6.0
|
|
ln -sf libvlc.so.5.6.0 /usr/lib/libvlc.so.5
|
|
ln -sf libvlc.so.5 /usr/lib/libvlc.so
|
|
rm -f /usr/lib/libvlc.so.5.6.1 2>/dev/null # remove real one if exists
|
|
else
|
|
# Fallback: keep one libvlc.so.5 if it exists from pacman
|
|
echo " WARNING: libvlc stub not found at /tmp/libvlc-stub.so — keeping system libvlc"
|
|
fi
|
|
echo " Removed: mali-egl, guile, vlccore, qt, git, cmake, glycin"
|
|
|
|
# --- Unused binaries ---
|
|
for bin in gcc g++ cc c++ cpp gccgo go gofmt \
|
|
clang clangd clang++ clang-format clang-tidy c-index-test \
|
|
cmake cmake-gui ctest cpack ccmake \
|
|
make gmake perl perl5 guile guild git git-upload-pack git-receive-pack \
|
|
ld ld.bfd ld.gold as objdump objcopy strip ranlib ar nm readelf \
|
|
c++filt addr2line size strings gprof flex bison yacc m4 \
|
|
autoconf automake autoreconf aclocal autoheader autoscan \
|
|
libtool libtoolize pkg-config pkgconf \
|
|
xgettext msgfmt msgmerge msginit gettext ngettext envsubst \
|
|
obj2yaml rsvg-convert vlc cvlc nvlc rvlc svlc; do
|
|
rm -f "/usr/bin/$bin" 2>/dev/null
|
|
done
|
|
rm -f /usr/bin/llvm-* /usr/bin/clang-* /usr/bin/yaml2obj 2>/dev/null
|
|
echo " Removed build/tool binaries"
|
|
|
|
# --- Unused share data ---
|
|
rm -rf /usr/share/cmake /usr/share/guile /usr/share/gir-1.0 /usr/share/groff
|
|
rm -rf /usr/share/qt6 /usr/share/qt /usr/share/perl5
|
|
echo " Removed unused share data"
|
|
|
|
# --- Python cleanup (keep runtime, remove IDE/test/build) ---
|
|
PY="/usr/lib/python3.14"
|
|
rm -rf "$PY/config-3.14-aarch64-linux-gnu" "$PY/idlelib" "$PY/ensurepip"
|
|
rm -rf "$PY/test" "$PY/unittest" "$PY/tkinter" "$PY/turtle"* "$PY/turtledemo"
|
|
rm -rf "$PY/distutils" "$PY/pydoc"* "$PY/lib2to3" "$PY/doctest.py"
|
|
find "$PY" -name "__pycache__" -type d -exec rm -rf {} + 2>/dev/null
|
|
echo " Python cleaned (kept runtime only)"
|
|
|
|
# --- Mask systemd generators (50-100ms boot gain) ---
|
|
GENDIR="/etc/systemd/system-generators"
|
|
mkdir -p "$GENDIR"
|
|
for gen in systemd-cryptsetup-generator systemd-gpt-auto-generator \
|
|
systemd-debug-generator systemd-hibernate-resume-generator \
|
|
systemd-bless-boot-generator systemd-factory-reset-generator \
|
|
systemd-import-generator systemd-integritysetup-generator \
|
|
systemd-veritysetup-generator systemd-ssh-generator \
|
|
systemd-tpm2-generator systemd-run-generator \
|
|
systemd-system-update-generator; do
|
|
ln -sf /dev/null "$GENDIR/$gen"
|
|
done
|
|
mkdir -p /etc/systemd/user-generators
|
|
ln -sf /dev/null /etc/systemd/user-generators/systemd-xdg-autostart-generator
|
|
echo " Masked 14 systemd generators"
|
|
|
|
# --- Mask unnecessary sockets ---
|
|
for sock in systemd-networkd.socket systemd-networkd-varlink.socket \
|
|
systemd-networkd-resolve-hook.socket systemd-userdbd.socket \
|
|
systemd-resolved-monitor.socket systemd-resolved-varlink.socket; do
|
|
ln -sf /dev/null "/etc/systemd/system/$sock"
|
|
done
|
|
rm -rf /etc/systemd/system/sockets.target.wants
|
|
echo " Masked unnecessary socket activations"
|
|
|
|
# --- Mask unnecessary services and timers ---
|
|
ln -sf /dev/null /etc/systemd/system/polkit.service
|
|
ln -sf /dev/null /etc/systemd/system/systemd-userdbd.service
|
|
ln -sf /dev/null /etc/systemd/system/archlinux-keyring-wkd-sync.timer
|
|
echo " Masked polkit, userdbd, keyring-sync timer"
|
|
|
|
# --- Fix PAM: remove pam_access.so reference (module deleted in bloat cleanup) ---
|
|
# pam_access.so provides host/network-based access control — not needed on gaming handheld
|
|
# Without this fix, login fails with "Module is unknown" → autologin loop
|
|
sed -i '/pam_access.so/d' /etc/pam.d/system-login 2>/dev/null
|
|
echo " Fixed PAM: removed pam_access.so reference"
|
|
|
|
echo "=== Bloat removal complete ==="
|
|
|
|
echo "=== Chroot setup complete ==="
|
|
SETUP_EOF
|
|
|
|
chmod +x "$ROOTFS_DIR/tmp/setup.sh"
|
|
|
|
# Build libvlc stub into chroot (needed by bloat cleanup — ES links against libvlc.so.5)
|
|
aarch64-linux-gnu-gcc -shared -O2 -o "$ROOTFS_DIR/tmp/libvlc-stub.so" "$SCRIPT_DIR/scripts/vlc-stub.c"
|
|
aarch64-linux-gnu-strip "$ROOTFS_DIR/tmp/libvlc-stub.so"
|
|
|
|
# Copy modules.bad into chroot (installed by setup.sh to /etc/archr/)
|
|
cp "$SCRIPT_DIR/config/modules.bad" "$ROOTFS_DIR/tmp/modules.bad"
|
|
|
|
# Run setup inside chroot
|
|
log " Running setup inside chroot..."
|
|
chroot "$ROOTFS_DIR" /tmp/setup.sh
|
|
|
|
log " ✓ System configured"
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Step 5: Install Arch R Scripts and Configs
|
|
#------------------------------------------------------------------------------
|
|
log ""
|
|
log "Step 5: Installing Arch R scripts and configs..."
|
|
|
|
# Performance scripts
|
|
install -m 755 "$SCRIPT_DIR/scripts/perfmax" "$ROOTFS_DIR/usr/local/bin/perfmax"
|
|
install -m 755 "$SCRIPT_DIR/scripts/perfnorm" "$ROOTFS_DIR/usr/local/bin/perfnorm"
|
|
install -m 755 "$SCRIPT_DIR/scripts/pmic-poweroff" "$ROOTFS_DIR/usr/local/bin/pmic-poweroff"
|
|
install -m 755 "$SCRIPT_DIR/scripts/retroarch-launch.sh" "$ROOTFS_DIR/usr/local/bin/retroarch-launch"
|
|
# RetroArch core options (per-core tuning for RK3326)
|
|
install -m 644 "$SCRIPT_DIR/config/retroarch-core-options.cfg" "$ROOTFS_DIR/home/archr/.config/retroarch/retroarch-core-options.cfg"
|
|
log " ✓ RetroArch core options installed"
|
|
# Input merger daemon: combines gpio-keys + adc-joystick into single virtual device
|
|
# RetroArch needs all inputs on one device (udev driver assigns each to separate port)
|
|
if command -v aarch64-linux-gnu-gcc &>/dev/null; then
|
|
aarch64-linux-gnu-gcc -static -O2 -o "$ROOTFS_DIR/usr/local/bin/input-merge" \
|
|
"$SCRIPT_DIR/scripts/input-merge.c"
|
|
chmod 755 "$ROOTFS_DIR/usr/local/bin/input-merge"
|
|
log " ✓ input-merge compiled and installed"
|
|
# Gamepad-to-keyboard mapper for Linux ports (compatible with ROCKNIX .gptk configs)
|
|
aarch64-linux-gnu-gcc -static -O2 -o "$ROOTFS_DIR/usr/local/bin/archr-gptokeyb" \
|
|
"$SCRIPT_DIR/scripts/archr-gptokeyb.c"
|
|
chmod 755 "$ROOTFS_DIR/usr/local/bin/archr-gptokeyb"
|
|
log " ✓ archr-gptokeyb compiled and installed"
|
|
else
|
|
warn "aarch64-linux-gnu-gcc not found — input-merge/gptokeyb not compiled"
|
|
fi
|
|
# gptokeyb config files
|
|
mkdir -p "$ROOTFS_DIR/etc/archr/gptokeyb"
|
|
install -m 644 "$SCRIPT_DIR/config/gptokeyb/default.gptk" "$ROOTFS_DIR/etc/archr/gptokeyb/default.gptk"
|
|
install -m 644 "$SCRIPT_DIR/config/gptokeyb/tools.gptk" "$ROOTFS_DIR/etc/archr/gptokeyb/tools.gptk"
|
|
log " ✓ gptokeyb configs installed (default + tools)"
|
|
# RetroArch joypad autoconfig (merged device + individual fallbacks)
|
|
mkdir -p "$ROOTFS_DIR/usr/share/retroarch/autoconfig/udev"
|
|
install -m 644 "$SCRIPT_DIR/config/autoconfig/udev/archr-gamepad.cfg" "$ROOTFS_DIR/usr/share/retroarch/autoconfig/udev/"
|
|
install -m 644 "$SCRIPT_DIR/config/autoconfig/udev/gpio-keys.cfg" "$ROOTFS_DIR/usr/share/retroarch/autoconfig/udev/"
|
|
install -m 644 "$SCRIPT_DIR/config/autoconfig/udev/adc-joystick.cfg" "$ROOTFS_DIR/usr/share/retroarch/autoconfig/udev/"
|
|
# systemd shutdown hook — runs pmic-poweroff at the very end of shutdown sequence
|
|
# (after all services stopped + filesystems unmounted, before kernel halt)
|
|
mkdir -p "$ROOTFS_DIR/usr/lib/systemd/system-shutdown"
|
|
install -m 755 "$SCRIPT_DIR/scripts/pmic-shutdown-hook" "$ROOTFS_DIR/usr/lib/systemd/system-shutdown/pmic-poweroff"
|
|
log " ✓ Performance + power-off scripts installed (including systemd shutdown hook)"
|
|
|
|
# ES info bar scripts (called by ES-fcamod for status display)
|
|
install -m 755 "$SCRIPT_DIR/scripts/current_volume" "$ROOTFS_DIR/usr/local/bin/current_volume"
|
|
install -m 755 "$SCRIPT_DIR/scripts/current_brightness" "$ROOTFS_DIR/usr/local/bin/current_brightness"
|
|
log " ✓ ES info bar scripts installed (current_volume, current_brightness)"
|
|
|
|
# ES-fcamod dArkOS compatibility scripts (timezones, auto-suspend)
|
|
install -m 755 "$SCRIPT_DIR/scripts/timezones" "$ROOTFS_DIR/usr/local/bin/timezones"
|
|
install -m 755 "$SCRIPT_DIR/scripts/auto_suspend_update.sh" "$ROOTFS_DIR/usr/local/bin/auto_suspend_update.sh"
|
|
log " ✓ ES compatibility scripts installed (timezones, auto_suspend_update.sh)"
|
|
|
|
# System management scripts
|
|
install -m 755 "$SCRIPT_DIR/scripts/archr-automount" "$ROOTFS_DIR/usr/local/bin/archr-automount"
|
|
install -m 755 "$SCRIPT_DIR/scripts/archr-usbgadget" "$ROOTFS_DIR/usr/local/bin/archr-usbgadget"
|
|
install -m 755 "$SCRIPT_DIR/scripts/archr-bluetooth-agent" "$ROOTFS_DIR/usr/local/bin/archr-bluetooth-agent"
|
|
install -m 755 "$SCRIPT_DIR/scripts/archr-factory-reset" "$ROOTFS_DIR/usr/local/bin/archr-factory-reset"
|
|
install -m 755 "$SCRIPT_DIR/scripts/archr-suspend-mode" "$ROOTFS_DIR/usr/local/bin/archr-suspend-mode"
|
|
install -m 755 "$SCRIPT_DIR/scripts/archr-save-config" "$ROOTFS_DIR/usr/local/bin/archr-save-config"
|
|
install -m 755 "$SCRIPT_DIR/scripts/archr-memory-manager" "$ROOTFS_DIR/usr/local/bin/archr-memory-manager"
|
|
log " ✓ System management scripts installed"
|
|
|
|
# System sleep hook (called by systemd on suspend/resume)
|
|
mkdir -p "$ROOTFS_DIR/usr/lib/systemd/system-sleep"
|
|
install -m 755 "$SCRIPT_DIR/scripts/archr-sleep" "$ROOTFS_DIR/usr/lib/systemd/system-sleep/archr-sleep"
|
|
log " ✓ Sleep hook installed"
|
|
|
|
# ES Tools shared menu library (sourced by tool scripts)
|
|
mkdir -p "$ROOTFS_DIR/usr/lib/archr"
|
|
install -m 644 "$SCRIPT_DIR/scripts/opt-system/menu-lib.sh" "$ROOTFS_DIR/usr/lib/archr/menu-lib.sh"
|
|
|
|
# ES Tools menu scripts (shown in ES OPTIONS menu via GuiTools)
|
|
mkdir -p "$ROOTFS_DIR/opt/system"
|
|
for script in "$SCRIPT_DIR/scripts/opt-system/"*.sh; do
|
|
[ -f "$script" ] || continue
|
|
[ "$(basename "$script")" = "menu-lib.sh" ] && continue
|
|
install -m 755 "$script" "$ROOTFS_DIR/opt/system/$(basename "$script")"
|
|
done
|
|
log " ✓ ES Tools menu scripts installed (/opt/system/)"
|
|
|
|
# Udev rules
|
|
mkdir -p "$ROOTFS_DIR/etc/udev/rules.d"
|
|
install -m 644 "$SCRIPT_DIR/config/udev/99-archr-automount.rules" "$ROOTFS_DIR/etc/udev/rules.d/99-archr-automount.rules"
|
|
install -m 644 "$SCRIPT_DIR/config/udev/80-archr-usbgadget.rules" "$ROOTFS_DIR/etc/udev/rules.d/80-archr-usbgadget.rules"
|
|
log " ✓ Udev rules installed (automount, usbgadget)"
|
|
|
|
# Install prebuilt libretro cores (not available in ALARM repos)
|
|
PREBUILT_CORES="$SCRIPT_DIR/prebuilt/cores"
|
|
if [ -d "$PREBUILT_CORES" ]; then
|
|
mkdir -p "$ROOTFS_DIR/usr/lib/libretro"
|
|
for core in "$PREBUILT_CORES"/*_libretro.so; do
|
|
[ -f "$core" ] || continue
|
|
install -m 644 "$core" "$ROOTFS_DIR/usr/lib/libretro/"
|
|
log " Prebuilt core: $(basename "$core")"
|
|
done
|
|
fi
|
|
|
|
# Distro version for ES info bar (ES reads title= from this file)
|
|
mkdir -p "$ROOTFS_DIR/usr/share/plymouth/themes"
|
|
echo "title=Arch R v1.0 ($(date +%Y-%m-%d))" > "$ROOTFS_DIR/usr/share/plymouth/themes/text.plymouth"
|
|
log " ✓ Distro version installed (text.plymouth)"
|
|
|
|
# First boot script
|
|
install -m 755 "$SCRIPT_DIR/scripts/first-boot.sh" "$ROOTFS_DIR/usr/local/bin/first-boot.sh"
|
|
log " ✓ First boot script installed"
|
|
|
|
# Boot splash: no binary needed in rootfs — splash is embedded in initramfs /init
|
|
# (built by build-image.sh: archr-init.c + embedded splash BMP → initramfs.img)
|
|
|
|
# RetroArch config (install to user's config dir where retroarch expects it)
|
|
mkdir -p "$ROOTFS_DIR/home/archr/.config/retroarch"
|
|
cp "$SCRIPT_DIR/config/retroarch.cfg" "$ROOTFS_DIR/home/archr/.config/retroarch/retroarch.cfg"
|
|
log " ✓ RetroArch config installed"
|
|
|
|
# Archr system config directory
|
|
mkdir -p "$ROOTFS_DIR/etc/archr"
|
|
|
|
# SDL GameController DB for R36S (gpio-keys + adc-joystick)
|
|
cp "$SCRIPT_DIR/config/gamecontrollerdb.txt" "$ROOTFS_DIR/etc/archr/gamecontrollerdb.txt"
|
|
log " ✓ GameController DB installed"
|
|
|
|
# EmulationStation configs
|
|
mkdir -p "$ROOTFS_DIR/etc/emulationstation"
|
|
if [ -f "$SCRIPT_DIR/config/es_systems.cfg" ]; then
|
|
cp "$SCRIPT_DIR/config/es_systems.cfg" "$ROOTFS_DIR/etc/emulationstation/"
|
|
log " ✓ ES systems config installed"
|
|
fi
|
|
if [ -f "$SCRIPT_DIR/config/es_input.cfg" ]; then
|
|
cp "$SCRIPT_DIR/config/es_input.cfg" "$ROOTFS_DIR/etc/emulationstation/"
|
|
log " ✓ ES input config installed (gpio-keys + adc-joystick)"
|
|
fi
|
|
|
|
# Battery LED warning script
|
|
install -m 755 "$SCRIPT_DIR/scripts/batt_life_warning.py" "$ROOTFS_DIR/usr/local/bin/batt_life_warning.py"
|
|
log " ✓ Battery LED script installed"
|
|
|
|
# Hotkey daemon (volume/brightness control)
|
|
install -m 755 "$SCRIPT_DIR/scripts/archr-hotkeys.py" "$ROOTFS_DIR/usr/local/bin/archr-hotkeys.py"
|
|
log " ✓ Hotkey daemon installed"
|
|
|
|
# Screenshots directory (used by MODE+B hotkey)
|
|
mkdir -p "$ROOTFS_DIR/home/archr/screenshots"
|
|
|
|
# Fix ownership of archr home directory (files installed by root in Step 5)
|
|
chown -R 1001:1001 "$ROOTFS_DIR/home/archr"
|
|
log " ✓ archr home ownership fixed (UID 1001)"
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Step 6: Install Kernel and Modules
|
|
#------------------------------------------------------------------------------
|
|
log ""
|
|
log "Step 6: Installing kernel and modules..."
|
|
|
|
KERNEL_BOOT="$OUTPUT_DIR/boot"
|
|
KERNEL_MODULES="$OUTPUT_DIR/modules/lib/modules"
|
|
|
|
if [ -f "$KERNEL_BOOT/KERNEL" ]; then
|
|
mkdir -p "$ROOTFS_DIR/boot"
|
|
cp "$KERNEL_BOOT/KERNEL" "$ROOTFS_DIR/boot/"
|
|
log " ✓ Kernel installed to rootfs/boot/"
|
|
|
|
# Copy board DTBs (build-image.sh handles BOOT partition separately)
|
|
if [ -d "$KERNEL_BOOT/dtbs" ]; then
|
|
mkdir -p "$ROOTFS_DIR/boot/dtbs"
|
|
for dtb in "$KERNEL_BOOT/dtbs"/rk3326-*.dtb; do
|
|
[ -f "$dtb" ] && cp "$dtb" "$ROOTFS_DIR/boot/dtbs/"
|
|
done
|
|
dtb_count=$(ls "$ROOTFS_DIR/boot/dtbs"/rk3326-*.dtb 2>/dev/null | wc -l)
|
|
log " ✓ $dtb_count DTBs installed to rootfs/boot/dtbs/"
|
|
fi
|
|
else
|
|
warn "Kernel not found at $KERNEL_BOOT/KERNEL. Run build-kernel.sh first!"
|
|
fi
|
|
|
|
if [ -d "$KERNEL_MODULES" ]; then
|
|
cp -r "$KERNEL_MODULES"/* "$ROOTFS_DIR/lib/modules/"
|
|
log " ✓ Kernel modules installed"
|
|
|
|
# Fix kernel version / modules directory mismatch (-dirty suffix)
|
|
# Kernel may report version with -dirty suffix but modules dir lacks it
|
|
for moddir in "$ROOTFS_DIR/lib/modules/"*; do
|
|
[ -d "$moddir" ] || continue
|
|
base=$(basename "$moddir")
|
|
dirty="${base}-dirty"
|
|
if [ ! -e "$ROOTFS_DIR/lib/modules/$dirty" ]; then
|
|
ln -sf "$base" "$ROOTFS_DIR/lib/modules/$dirty"
|
|
log " ✓ Modules symlink: $dirty -> $base"
|
|
fi
|
|
done
|
|
else
|
|
warn "Kernel modules not found. Run build-kernel.sh first!"
|
|
fi
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Step 7: Cleanup
|
|
#------------------------------------------------------------------------------
|
|
log ""
|
|
log "Step 7: Cleaning up..."
|
|
|
|
# Remove setup script
|
|
rm -f "$ROOTFS_DIR/tmp/setup.sh"
|
|
|
|
# Remove QEMU
|
|
rm -f "$ROOTFS_DIR/usr/bin/qemu-aarch64-static"
|
|
|
|
# Bind mounts are cleaned up by the EXIT trap (cleanup_mounts)
|
|
|
|
log " ✓ Cleanup complete"
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Summary
|
|
#------------------------------------------------------------------------------
|
|
log ""
|
|
log "=== Rootfs Build Complete ==="
|
|
|
|
ROOTFS_SIZE=$(du -sh "$ROOTFS_DIR" | cut -f1)
|
|
log ""
|
|
log "Rootfs location: $ROOTFS_DIR"
|
|
log "Rootfs size: $ROOTFS_SIZE"
|
|
log ""
|
|
log "✓ Arch R rootfs ready!"
|