diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 482a5fd..472b23d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,16 +50,15 @@ jobs: fi - name: Setup z/VM environment + env: + ZVM_HOST: ${{ secrets.ZVM_HOST }} + ZVM_USER: ${{ secrets.ZVM_USER }} + ZVM_SSH_KEY: ${{ secrets.ZVM_SSH_KEY }} run: | - # Load z/VM configuration from secret - echo "${{ secrets.ZVM_CONFIG }}" > .env.temp - source .env.temp - rm .env.temp - # Validate required variables if [ -z "$ZVM_HOST" ] || [ -z "$ZVM_USER" ] || [ -z "$ZVM_SSH_KEY" ]; then echo "Error: Missing required z/VM configuration" - echo "ZVM_CONFIG secret must contain: ZVM_HOST, ZVM_USER, ZVM_SSH_KEY" + echo "Required secrets: ZVM_HOST, ZVM_USER, ZVM_SSH_KEY" exit 1 fi diff --git a/.gitignore b/.gitignore index 7439d03..51fe959 100644 --- a/.gitignore +++ b/.gitignore @@ -67,9 +67,8 @@ buildconfig config VERSION -# Documentation -CLAUDE.md -PROJECT_SUMMARY.md +# Documentation WiP +docs/* # Security *.pem @@ -78,6 +77,9 @@ PROJECT_SUMMARY.md # Test artifacts boot/rootfs-s390x.img +# Systemd documentation +docs/systemd-s390x-patches.md + # Build artifacts in boot directory (regenerated by make) boot/vmlinuz-linux boot/initramfs-linux.img diff --git a/Makefile b/Makefile index 7902122..658c5cd 100644 --- a/Makefile +++ b/Makefile @@ -60,36 +60,35 @@ container: @echo "Building s390x-archlinux-dev container..." @./scripts/build-container.sh -# Test with QEMU (assumes boot files already exist) -test: +# Common validation for test targets +define check_boot_files @if [ ! -f "$(BOOT_DIR)/vmlinuz-linux" ] || [ ! -f "$(BOOT_DIR)/initramfs-linux.img" ]; then \ echo "Error: Boot files not found. Run 'make all' first"; \ exit 1; \ fi - @echo "Testing s390x system with QEMU..." - @./scripts/run-qemu-initramfs-only.sh +endef -# Test with root filesystem (creates minimal rootfs and boots to it) +# Test with QEMU (initramfs-only) +test: + $(call check_boot_files) + @echo "Testing s390x system with QEMU (initramfs-only)..." + @./scripts/test-qemu.sh initramfs + +# Test with root filesystem test-rootfs: - @if [ ! -f "$(BOOT_DIR)/vmlinuz-linux" ] || [ ! -f "$(BOOT_DIR)/initramfs-linux.img" ]; then \ - echo "Error: Boot files not found. Run 'make all' first"; \ - exit 1; \ - fi + $(call check_boot_files) @echo "Testing s390x system with root filesystem..." - @./scripts/test-qemu-rootfs.sh + @./scripts/test-qemu.sh rootfs # Test with systemd as init test-systemd: - @if [ ! -f "$(BOOT_DIR)/vmlinuz-linux" ] || [ ! -f "$(BOOT_DIR)/initramfs-linux.img" ]; then \ - echo "Error: Boot files not found. Run 'make all' first"; \ - exit 1; \ - fi + $(call check_boot_files) @if [ ! -d "output/systemd-root" ]; then \ echo "Error: Systemd not found. Run 'make systemd' first"; \ exit 1; \ fi @echo "Testing s390x system with systemd..." - @./scripts/test-qemu-systemd.sh + @./scripts/test-qemu.sh systemd # Create directories $(OUTPUT_DIR): diff --git a/boot/boot-info.txt b/boot/boot-info.txt index b168ad9..cc957de 100644 --- a/boot/boot-info.txt +++ b/boot/boot-info.txt @@ -2,7 +2,7 @@ Arch Linux s390x Boot Directory ============================== Kernel Version: 6.6.10 -Build Date: Thu Jun 5 11:58:54 AM CEST 2025 +Build Date: Thu Jun 5 03:04:40 PM CEST 2025 Architecture: s390x Files: diff --git a/boot/vmlinuz-linux b/boot/vmlinuz-linux index 1acbf4a..7b5af8b 100755 Binary files a/boot/vmlinuz-linux and b/boot/vmlinuz-linux differ diff --git a/docs/systemd-s390x-patches.md b/docs/systemd-s390x-patches.md deleted file mode 100644 index 30eb750..0000000 --- a/docs/systemd-s390x-patches.md +++ /dev/null @@ -1,105 +0,0 @@ -# systemd s390x Big-Endian Patches - -## Overview - -When building systemd for s390x architecture, several big-endian compatibility issues need to be addressed. This document outlines known issues and required patches based on research from enterprise distributions. - -## Known Issues - -### 1. D-Bus Property Endianness - -**Issue**: D-Bus property values may be incorrectly interpreted due to endianness -**Symptom**: Values like 131074 (0x20002) instead of expected values -**Affected Components**: systemd-resolved, systemd property queries - -**Patch Required**: -```patch ---- a/src/libsystemd/sd-bus/bus-message.c -+++ b/src/libsystemd/sd-bus/bus-message.c -@@ -1234,7 +1234,7 @@ static int message_append_basic(sd_bus_message *m, char type, const void *p, co - case SD_BUS_TYPE_UINT32: - case SD_BUS_TYPE_UNIX_FD: -- u32 = *(uint32_t*) p; -+ u32 = be32toh(*(uint32_t*) p); - p = &u32; - align = 4; - sz = 4; -``` - -### 2. systemd-resolved Big-Endian Failures - -**Issue**: systemd-resolved fails on big-endian architectures -**Symptom**: Service fails to start, D-Bus communication errors -**Solution**: Disable systemd-resolved in build configuration (already done in our minimal build) - -### 3. Integration Test Timeouts - -**Issue**: Tests timeout more frequently on s390x -**Symptom**: Unit tests fail with timeout errors -**Solution**: Increase timeout values or disable tests (already done in our build) - -### 4. /dev/kvm Permissions - -**Issue**: Incorrect permissions on /dev/kvm device -**Symptom**: KVM acceleration not available to non-root users -**Solution**: Add udev rule for proper permissions - -```bash -# /etc/udev/rules.d/99-kvm-s390x.rules -KERNEL=="kvm", GROUP="kvm", MODE="0660" -``` - -## Distribution-Specific Patches - -### SUSE/openSUSE - -SUSE maintains comprehensive s390x patches in their systemd package: -- Endianness fixes for D-Bus communication -- s390x-specific device handling -- Console output improvements - -### Red Hat/Fedora - -Red Hat includes patches for: -- Big-endian compatibility in property serialization -- s390x hardware console support -- Device node permission fixes - -### Ubuntu - -Ubuntu patches focus on: -- Integration test reliability -- Hardware-specific optimizations -- Kernel feature detection - -## Implementation Strategy - -1. **Start with minimal build** - Our configuration already disables problematic components -2. **Monitor logs carefully** - Use `systemd.log_level=debug` during initial testing -3. **Apply patches incrementally** - Test after each patch to isolate issues -4. **Use distribution patches** - Leverage existing work from SUSE/Red Hat/Ubuntu - -## Testing Recommendations - -```bash -# Enable comprehensive debugging -systemd.log_level=debug -systemd.journald.forward_to_console=1 -console=ttysclp0 - -# Test in container first -systemd-nspawn -bD /test-root --machine=s390x-test - -# Monitor for endianness issues -journalctl -f | grep -E "(endian|0x[0-9a-f]+|property|dbus)" -``` - -## Resources - -- [SUSE systemd s390x patches](https://build.opensuse.org/package/view_file/Base:System/systemd/) -- [Red Hat Bugzilla - s390x systemd issues](https://bugzilla.redhat.com/buglist.cgi?product=Fedora&component=systemd&bug_status=__open__&f1=cf_machine&o1=substring&v1=s390x) -- [systemd upstream s390x issues](https://github.com/systemd/systemd/issues?q=is%3Aissue+s390x) - -## Conclusion - -While s390x support in systemd requires patches for full functionality, the core init system works reliably. By starting with a minimal build and gradually adding components, we can achieve a stable systemd deployment for Arch Linux s390x. \ No newline at end of file diff --git a/scripts/build-busybox-zvm.sh b/scripts/build-busybox-zvm.sh index 5c39e5c..f896036 100755 --- a/scripts/build-busybox-zvm.sh +++ b/scripts/build-busybox-zvm.sh @@ -1,9 +1,19 @@ #!/bin/bash # Build static busybox on z/VM s390x RHEL system +# SPDX-License-Identifier: GPL-2.0-only -set -e +# Source utilities (only common.sh since this runs on z/VM) +if [ -f "scripts/common.sh" ]; then + source scripts/common.sh +else + # Minimal logging for z/VM environment + log_info() { echo "[INFO] $1"; } + log_success() { echo "[SUCCESS] $1"; } + log_error() { echo "[ERROR] $1" >&2; } + die() { log_error "$1"; exit "${2:-1}"; } +fi -echo "Building static busybox on native s390x..." +log_info "Building static busybox on native s390x..." # Install build dependencies sudo dnf install -y gcc make wget tar bzip2 glibc-static diff --git a/scripts/common.sh b/scripts/common.sh new file mode 100644 index 0000000..87a02d4 --- /dev/null +++ b/scripts/common.sh @@ -0,0 +1,286 @@ +#!/bin/bash +# Common utilities for Arch Linux s390x build system +# SPDX-License-Identifier: GPL-2.0-only + +# Color definitions +export RED='\033[0;31m' +export GREEN='\033[0;32m' +export YELLOW='\033[1;33m' +export BLUE='\033[0;34m' +export MAGENTA='\033[0;35m' +export CYAN='\033[0;36m' +export NC='\033[0m' + +# Get project root directory +get_project_root() { + local script_dir="$(cd "$(dirname "${BASH_SOURCE[1]}")" && pwd)" + echo "$(dirname "$script_dir")" +} + +# Get project paths +get_project_paths() { + export PROJECT_ROOT="$(get_project_root)" + export OUTPUT_DIR="$PROJECT_ROOT/output" + export BOOT_DIR="$PROJECT_ROOT/boot" + export BUILD_DIR="$PROJECT_ROOT/build-kernel" + export SCRIPTS_DIR="$PROJECT_ROOT/scripts" + export PATCHES_DIR="$PROJECT_ROOT/patches" + export CONFIG_DIR="$PROJECT_ROOT/config" +} + +# Logging functions +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" >&2 +} + +log_debug() { + if [ "${DEBUG:-0}" = "1" ]; then + echo -e "${MAGENTA}[DEBUG]${NC} $1" >&2 + fi +} + +# Error handling +die() { + log_error "$1" + exit "${2:-1}" +} + +# Check if command exists +command_exists() { + command -v "$1" >/dev/null 2>&1 +} + +# Check if running in container +is_container() { + [ -f /.dockerenv ] || [ -f /run/.containerenv ] || [ "${container:-}" = "podman" ] +} + +# Validate required commands +check_required_commands() { + local missing=() + for cmd in "$@"; do + if ! command_exists "$cmd"; then + missing+=("$cmd") + fi + done + + if [ ${#missing[@]} -gt 0 ]; then + log_error "Missing required commands: ${missing[*]}" + return 1 + fi +} + +# Validate required files +check_required_files() { + local missing=() + for file in "$@"; do + if [ ! -f "$file" ]; then + missing+=("$file") + fi + done + + if [ ${#missing[@]} -gt 0 ]; then + log_error "Missing required files: ${missing[*]}" + return 1 + fi +} + +# Validate required directories +check_required_dirs() { + local missing=() + for dir in "$@"; do + if [ ! -d "$dir" ]; then + missing+=("$dir") + fi + done + + if [ ${#missing[@]} -gt 0 ]; then + log_error "Missing required directories: ${missing[*]}" + return 1 + fi +} + +# Create directory if it doesn't exist +ensure_dir() { + local dir="$1" + if [ ! -d "$dir" ]; then + log_debug "Creating directory: $dir" + mkdir -p "$dir" || die "Failed to create directory: $dir" + fi +} + +# Load environment from .env file if it exists +load_env() { + local env_file="${1:-.env}" + if [ -f "$env_file" ]; then + log_debug "Loading environment from $env_file" + set -a + source "$env_file" + set +a + log_debug "Environment loaded successfully" + else + log_debug "No .env file found at $env_file" + fi +} + +# Print script header +print_header() { + local title="$1" + local width=60 + echo + echo -e "${CYAN}$(printf '=%.0s' $(seq 1 $width))${NC}" + echo -e "${CYAN}$(printf '%-*s' $width "$title")${NC}" + echo -e "${CYAN}$(printf '=%.0s' $(seq 1 $width))${NC}" + echo +} + +# Print section separator +print_section() { + local title="$1" + echo + echo -e "${YELLOW}=== $title ===${NC}" +} + +# Cleanup function for temporary files +cleanup_on_exit() { + local temp_files=("$@") + for file in "${temp_files[@]}"; do + if [ -f "$file" ]; then + log_debug "Cleaning up temporary file: $file" + rm -f "$file" + fi + done +} + +# Set up cleanup trap +setup_cleanup() { + local temp_files=("$@") + trap 'cleanup_on_exit "${temp_files[@]}"' EXIT INT TERM +} + +# Progress indicator +show_progress() { + local current="$1" + local total="$2" + local message="$3" + local width=40 + local progress=$((current * width / total)) + local remaining=$((width - progress)) + + printf "\r${CYAN}[%s%s] %d/%d %s${NC}" \ + "$(printf '#%.0s' $(seq 1 $progress))" \ + "$(printf '.%.0s' $(seq 1 $remaining))" \ + "$current" "$total" "$message" + + if [ "$current" -eq "$total" ]; then + echo + fi +} + +# Timer functions +start_timer() { + export TIMER_START=$(date +%s) +} + +end_timer() { + local start_time="${TIMER_START:-$(date +%s)}" + local end_time=$(date +%s) + local duration=$((end_time - start_time)) + local minutes=$((duration / 60)) + local seconds=$((duration % 60)) + + if [ $minutes -gt 0 ]; then + echo -e "${CYAN}Completed in ${minutes}m ${seconds}s${NC}" + else + echo -e "${CYAN}Completed in ${seconds}s${NC}" + fi +} + +# File size formatting +format_size() { + local size="$1" + if [ "$size" -ge 1073741824 ]; then + echo "$((size / 1073741824))GB" + elif [ "$size" -ge 1048576 ]; then + echo "$((size / 1048576))MB" + elif [ "$size" -ge 1024 ]; then + echo "$((size / 1024))KB" + else + echo "${size}B" + fi +} + +# Get file size in human readable format +get_file_size() { + local file="$1" + if [ -f "$file" ]; then + local size=$(stat -c%s "$file" 2>/dev/null || stat -f%z "$file" 2>/dev/null) + format_size "$size" + else + echo "N/A" + fi +} + +# Common QEMU configuration +run_qemu_s390x() { + local append_args="$1" + local extra_args="${2:-}" + + check_required_commands qemu-system-s390x + check_required_files "boot/vmlinuz-linux" "boot/initramfs-linux.img" + + log_info "Starting QEMU s390x system..." + log_debug "Append args: $append_args" + log_debug "Extra args: $extra_args" + + qemu-system-s390x \ + -machine s390-ccw-virtio \ + -cpu max \ + -m 2G \ + -kernel "boot/vmlinuz-linux" \ + -initrd "boot/initramfs-linux.img" \ + -append "console=ttyS0 $append_args" \ + -nographic \ + -device virtio-net-ccw,netdev=net0 \ + -netdev user,id=net0 \ + $extra_args +} + +# Initialize common environment +init_common() { + # Set strict error handling + set -euo pipefail + + # Initialize paths + get_project_paths + + # Load environment if available + load_env + + # Ensure output directory exists + ensure_dir "$OUTPUT_DIR" +} + +# Export functions for use in other scripts +export -f get_project_root get_project_paths +export -f log_info log_success log_warning log_error log_debug die +export -f command_exists is_container +export -f check_required_commands check_required_files check_required_dirs +export -f ensure_dir load_env +export -f print_header print_section +export -f cleanup_on_exit setup_cleanup +export -f show_progress start_timer end_timer +export -f format_size get_file_size +export -f run_qemu_s390x init_common \ No newline at end of file diff --git a/scripts/create-rootfs.sh b/scripts/create-rootfs.sh new file mode 100644 index 0000000..d03d744 --- /dev/null +++ b/scripts/create-rootfs.sh @@ -0,0 +1,581 @@ +#!/bin/bash +# Root filesystem creation utilities for Arch Linux s390x +# SPDX-License-Identifier: GPL-2.0-only + +# Source common utilities +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/common.sh" + +# Root filesystem configuration +export DEFAULT_ROOTFS_SIZE="150" +export DEFAULT_ROOTFS_NAME="rootfs-s390x.img" +export ROOTFS_MOUNT_POINT="/tmp/rootfs-mount" + +# Create basic root filesystem structure +create_rootfs_structure() { + local mount_point="$1" + + log_info "Creating root filesystem directory structure..." + + # Create standard Linux directory structure + local dirs=( + "bin" "sbin" "etc" "proc" "sys" "dev" "tmp" "var" "usr/bin" "usr/sbin" + "lib" "lib64" "run" "boot" "home" "root" "opt" "srv" "mnt" "media" + "etc/systemd/system" "var/log" "var/log/journal" "var/tmp" + "usr/lib" "usr/lib64" "usr/share" "usr/include" "usr/src" + ) + + for dir in "${dirs[@]}"; do + sudo mkdir -p "$mount_point/$dir" + log_debug "Created directory: $dir" + done + + log_success "Directory structure created" +} + +# Create essential device nodes +create_device_nodes() { + local mount_point="$1" + + log_info "Creating essential device nodes..." + + # Console and TTY devices + sudo mknod "$mount_point/dev/console" c 5 1 2>/dev/null || true + sudo mknod "$mount_point/dev/tty" c 5 0 2>/dev/null || true + + # TTY devices for virtual terminals + for i in {1..4}; do + sudo mknod "$mount_point/dev/tty$i" c 4 $i 2>/dev/null || true + done + + # Null and zero devices + sudo mknod "$mount_point/dev/null" c 1 3 2>/dev/null || true + sudo mknod "$mount_point/dev/zero" c 1 5 2>/dev/null || true + sudo mknod "$mount_point/dev/random" c 1 8 2>/dev/null || true + sudo mknod "$mount_point/dev/urandom" c 1 9 2>/dev/null || true + + log_success "Device nodes created" +} + +# Create basic system files +create_system_files() { + local mount_point="$1" + + log_info "Creating basic system files..." + + # /etc/passwd + sudo tee "$mount_point/etc/passwd" > /dev/null << 'EOF' +root:x:0:0:root:/root:/bin/sh +bin:x:1:1:bin:/bin:/usr/bin/nologin +daemon:x:2:2:daemon:/:/usr/bin/nologin +nobody:x:65534:65534:Kernel Overflow User:/:/usr/bin/nologin +EOF + + # /etc/group + sudo tee "$mount_point/etc/group" > /dev/null << 'EOF' +root:x:0: +bin:x:1: +daemon:x:2: +sys:x:3: +adm:x:4: +tty:x:5: +disk:x:6: +lp:x:7: +mail:x:8: +news:x:9: +uucp:x:10: +man:x:12: +proxy:x:13: +kmem:x:15: +dialout:x:20: +fax:x:21: +voice:x:22: +cdrom:x:24: +floppy:x:25: +tape:x:26: +sudo:x:27: +audio:x:29: +dip:x:30: +www-data:x:33: +backup:x:34: +operator:x:37: +list:x:38: +irc:x:39: +src:x:40: +gnats:x:41: +shadow:x:42: +utmp:x:43: +video:x:44: +sasl:x:45: +plugdev:x:46: +staff:x:50: +games:x:60: +users:x:100: +nogroup:x:65534: +EOF + + # /etc/shadow + sudo tee "$mount_point/etc/shadow" > /dev/null << 'EOF' +root::19797:0:99999:7::: +bin:*:19797:0:99999:7::: +daemon:*:19797:0:99999:7::: +nobody:*:19797:0:99999:7::: +EOF + + # /etc/os-release (Arch Linux identification) + sudo tee "$mount_point/etc/os-release" > /dev/null << 'EOF' +NAME="Arch Linux" +PRETTY_NAME="Arch Linux" +ID=arch +BUILD_ID=rolling +ANSI_COLOR="38;2;23;147;209" +HOME_URL="https://archlinux.org/" +DOCUMENTATION_URL="https://wiki.archlinux.org/" +SUPPORT_URL="https://bbs.archlinux.org/" +BUG_REPORT_URL="https://bugs.archlinux.org/" +PRIVACY_POLICY_URL="https://terms.archlinux.org/docs/privacy-policy/" +LOGO=archlinux-logo +EOF + + # /etc/arch-release + sudo tee "$mount_point/etc/arch-release" > /dev/null << 'EOF' +Arch Linux \r (\l) +EOF + + # /etc/hostname + sudo tee "$mount_point/etc/hostname" > /dev/null << 'EOF' +archlinux-s390x +EOF + + # /etc/hosts + sudo tee "$mount_point/etc/hosts" > /dev/null << 'EOF' +127.0.0.1 localhost +::1 localhost +127.0.1.1 archlinux-s390x.localdomain archlinux-s390x +EOF + + # /etc/fstab + sudo tee "$mount_point/etc/fstab" > /dev/null << 'EOF' +# Static information about the filesystems. +# See fstab(5) for details. + +# +/dev/vda / ext4 rw,relatime 0 1 +proc /proc proc defaults 0 0 +sysfs /sys sysfs defaults 0 0 +devtmpfs /dev devtmpfs defaults 0 0 +tmpfs /tmp tmpfs defaults 0 0 +EOF + + log_success "System files created" +} + +# Install busybox with applets +install_busybox() { + local mount_point="$1" + local busybox_binary="${2:-boot/busybox-s390x-static}" + + log_info "Installing busybox..." + + if [ ! -f "$busybox_binary" ]; then + log_error "Busybox binary not found: $busybox_binary" + return 1 + fi + + # Copy busybox binary + sudo cp "$busybox_binary" "$mount_point/bin/busybox" + sudo chmod +x "$mount_point/bin/busybox" + + # Install busybox applets + log_info "Installing busybox applets..." + sudo chroot "$mount_point" /bin/busybox --install -s + + # Verify critical applets and create missing ones + local critical_applets=("sh" "mount" "umount" "switch_root" "init") + for applet in "${critical_applets[@]}"; do + if [ -L "$mount_point/bin/$applet" ] || [ -f "$mount_point/bin/$applet" ]; then + log_debug "Applet installed: $applet" + else + log_debug "Creating missing applet symlink: $applet" + sudo ln -sf busybox "$mount_point/bin/$applet" + fi + done + + log_success "Busybox installed with applets" +} + +# Install systemd components +install_systemd() { + local mount_point="$1" + local systemd_root="${2:-output/systemd-root}" + + log_info "Installing systemd components..." + + if [ ! -d "$systemd_root" ]; then + log_error "Systemd root directory not found: $systemd_root" + return 1 + fi + + # Copy systemd files + sudo cp -r "$systemd_root"/* "$mount_point/" + + # Create systemd directories + sudo mkdir -p "$mount_point/run/systemd" + sudo mkdir -p "$mount_point/var/log/journal" + sudo mkdir -p "$mount_point/etc/systemd/system" + sudo mkdir -p "$mount_point/usr/lib/systemd/system" + + # Set permissions + sudo chmod 755 "$mount_point/usr/lib/systemd/systemd" + + # Configure systemd for easy access + configure_systemd_for_testing "$mount_point" + + log_success "Systemd components installed" +} + +# Configure systemd for easier testing access +configure_systemd_for_testing() { + local mount_point="$1" + + log_info "Configuring systemd for testing access..." + + # Set root password to empty (no password required) + sudo sed -i 's/^root::/root::/' "$mount_point/etc/shadow" + + # Disable first boot wizard by creating marker files + sudo mkdir -p "$mount_point/var/lib/systemd" + + # Create machine-id to skip first boot setup (as file, not directory) + sudo sh -c "uuidgen | tr -d '-' > '$mount_point/etc/machine-id'" + sudo touch "$mount_point/var/lib/systemd/first-boot" + + # Create a simple auto-login service for ttyS0 + sudo tee "$mount_point/etc/systemd/system/autologin-ttyS0.service" > /dev/null << 'EOF' +[Unit] +Description=Auto Login on ttyS0 +After=systemd-user-sessions.service plymouth-quit-wait.service +Before=getty.target + +[Service] +ExecStart=/bin/sh -c 'echo "Auto-login as root on ttyS0"; exec /bin/sh' +Type=idle +Restart=always +RestartSec=0 +StandardInput=tty +StandardOutput=tty +StandardError=tty +TTYPath=/dev/ttyS0 +TTYReset=yes +TTYVHangup=yes + +[Install] +WantedBy=getty.target +EOF + + # Create a simple auto-login service for ttysclp0 + sudo tee "$mount_point/etc/systemd/system/autologin-ttysclp0.service" > /dev/null << 'EOF' +[Unit] +Description=Auto Login on ttysclp0 +After=systemd-user-sessions.service plymouth-quit-wait.service +Before=getty.target + +[Service] +ExecStart=/bin/sh -c 'echo "Auto-login as root on ttysclp0"; exec /bin/sh' +Type=idle +Restart=always +RestartSec=0 +StandardInput=tty +StandardOutput=tty +StandardError=tty +TTYPath=/dev/ttysclp0 +TTYReset=yes +TTYVHangup=yes + +[Install] +WantedBy=getty.target +EOF + + # Disable/mask services that interfere with testing + sudo mkdir -p "$mount_point/etc/systemd/system" + sudo ln -sf "/dev/null" "$mount_point/etc/systemd/system/systemd-firstboot.service" + sudo ln -sf "/dev/null" "$mount_point/etc/systemd/system/getty@tty1.service" + sudo ln -sf "/dev/null" "$mount_point/etc/systemd/system/serial-getty@ttyS0.service" + sudo ln -sf "/dev/null" "$mount_point/etc/systemd/system/serial-getty@ttysclp0.service" + + # Enable the auto-login services + sudo mkdir -p "$mount_point/etc/systemd/system/getty.target.wants" + sudo ln -sf "/etc/systemd/system/autologin-ttyS0.service" "$mount_point/etc/systemd/system/getty.target.wants/" + sudo ln -sf "/etc/systemd/system/autologin-ttysclp0.service" "$mount_point/etc/systemd/system/getty.target.wants/" + + # Create a simple .bashrc for root to show system info + sudo tee "$mount_point/root/.bashrc" > /dev/null << 'EOF' +# Arch Linux s390x Root Shell +echo "Welcome to Arch Linux s390x with systemd!" +echo +echo "=== System Information ===" +echo "Architecture: $(uname -m)" +echo "Kernel: $(uname -r)" +echo "Systemd version: $(systemctl --version | head -n1)" +echo "Hostname: $(hostname)" +echo +echo "=== Systemd Status ===" +echo "Boot time: $(systemd-analyze | head -n1 2>/dev/null || echo 'Not available')" +echo "Failed units: $(systemctl --failed --no-legend | wc -l) unit(s)" +echo +echo "=== Available Commands ===" +echo "systemctl status - Show systemd status" +echo "systemctl --failed - Show failed services" +echo "journalctl -f - Follow system logs" +echo "poweroff - Shutdown system" +echo +EOF + + # Create emergency shell service + sudo tee "$mount_point/etc/systemd/system/emergency-shell.service" > /dev/null << 'EOF' +[Unit] +Description=Emergency Shell +DefaultDependencies=false +Conflicts=shutdown.target +Before=shutdown.target + +[Service] +Type=idle +ExecStart=/bin/sh +StandardInput=tty-force +StandardOutput=inherit +StandardError=inherit +KillMode=process + +[Install] +WantedBy=emergency.target +EOF + + # Add a service to show boot completion + sudo tee "$mount_point/etc/systemd/system/boot-complete.service" > /dev/null << 'EOF' +[Unit] +Description=Boot Completion Notification +After=multi-user.target + +[Service] +Type=oneshot +ExecStart=/bin/sh -c 'echo "🚀 Arch Linux s390x boot completed successfully!"' +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target +EOF + + # Enable the boot completion service + sudo mkdir -p "$mount_point/etc/systemd/system/multi-user.target.wants" + sudo ln -sf "/usr/lib/systemd/system/boot-complete.service" "$mount_point/etc/systemd/system/multi-user.target.wants/boot-complete.service" + + log_success "Systemd configured for testing" +} + +# Create init script +create_init_script() { + local mount_point="$1" + local use_systemd="${2:-false}" + + log_info "Creating init script (systemd: $use_systemd)..." + + if [ "$use_systemd" = "true" ]; then + # Create systemd init wrapper + sudo tee "$mount_point/sbin/init" > /dev/null << 'EOF' +#!/bin/sh +echo "Starting Arch Linux s390x with systemd..." + +# Mount essential filesystems +mount -t proc proc /proc 2>/dev/null || true +mount -t sysfs sysfs /sys 2>/dev/null || true +mount -t devtmpfs devtmpfs /dev 2>/dev/null || true +mount -t tmpfs tmpfs /run 2>/dev/null || true +mount -t tmpfs tmpfs /tmp 2>/dev/null || true + +# Create required directories +mkdir -p /run/systemd /var/log/journal + +echo "=== Starting systemd ===" +exec /usr/lib/systemd/systemd +EOF + else + # Create busybox init script + sudo tee "$mount_point/sbin/init" > /dev/null << 'EOF' +#!/bin/sh +echo "Welcome to Arch Linux s390x Root Filesystem!" + +# Mount essential filesystems +mount -t proc proc /proc 2>/dev/null || true +mount -t sysfs sysfs /sys 2>/dev/null || true +mount -t devtmpfs devtmpfs /dev 2>/dev/null || true + +# Create TTY devices if they don't exist +for i in 1 2 3 4; do + [ ! -c /dev/tty$i ] && mknod /dev/tty$i c 4 $i 2>/dev/null || true +done +[ ! -c /dev/console ] && mknod /dev/console c 5 1 2>/dev/null || true + +echo "=== ARCH LINUX VERIFICATION ===" +[ -f /etc/os-release ] && cat /etc/os-release +echo "Architecture: $(uname -m)" +echo "Kernel: $(uname -r)" +echo "Init: Busybox $(busybox | head -n1)" + +echo "" +echo "=== ROOT FILESYSTEM READY ===" +echo "You are now in the Arch Linux s390x root filesystem!" +echo "Available commands: $(busybox --list | tr '\n' ' ')" +echo "" + +# Start shell +exec /bin/sh +EOF + fi + + sudo chmod +x "$mount_point/sbin/init" + log_success "Init script created" +} + +# Mount root filesystem image +mount_rootfs() { + local rootfs_image="$1" + local mount_point="${2:-$ROOTFS_MOUNT_POINT}" + + log_info "Mounting root filesystem: $rootfs_image" + + if [ ! -f "$rootfs_image" ]; then + die "Root filesystem image not found: $rootfs_image" + fi + + # Create mount point + ensure_dir "$mount_point" + + # Check if already mounted + if mountpoint -q "$mount_point" 2>/dev/null; then + log_warning "Filesystem already mounted at $mount_point" + return 0 + fi + + # Mount the image + sudo mount -o loop "$rootfs_image" "$mount_point" || die "Failed to mount $rootfs_image" + + log_success "Filesystem mounted at $mount_point" +} + +# Unmount root filesystem +unmount_rootfs() { + local mount_point="${1:-$ROOTFS_MOUNT_POINT}" + + if mountpoint -q "$mount_point" 2>/dev/null; then + log_info "Unmounting filesystem at $mount_point" + sudo umount "$mount_point" || log_warning "Failed to unmount $mount_point" + rmdir "$mount_point" 2>/dev/null || true + log_success "Filesystem unmounted" + else + log_debug "No filesystem mounted at $mount_point" + fi +} + +# Create complete root filesystem +create_rootfs() { + local rootfs_image="$1" + local size_mb="${2:-$DEFAULT_ROOTFS_SIZE}" + local install_systemd="${3:-false}" + local busybox_binary="${4:-boot/busybox-s390x-static}" + local systemd_root="${5:-output/systemd-root}" + + print_header "Creating Root Filesystem: $rootfs_image" + start_timer + + # Cleanup any existing mount + unmount_rootfs + + # Remove existing image + if [ -f "$rootfs_image" ]; then + log_info "Removing existing root filesystem image..." + rm -f "$rootfs_image" + fi + + log_info "Creating ${size_mb}MB root filesystem image..." + + # Create and format filesystem + dd if=/dev/zero of="$rootfs_image" bs=1M count="$size_mb" status=progress + mkfs.ext4 -F "$rootfs_image" + + # Mount filesystem + mount_rootfs "$rootfs_image" + + # Create directory structure + create_rootfs_structure "$ROOTFS_MOUNT_POINT" + + # Create device nodes + create_device_nodes "$ROOTFS_MOUNT_POINT" + + # Create system files + create_system_files "$ROOTFS_MOUNT_POINT" + + # Install busybox + install_busybox "$ROOTFS_MOUNT_POINT" "$busybox_binary" + + # Install systemd if requested + if [ "$install_systemd" = "true" ]; then + install_systemd "$ROOTFS_MOUNT_POINT" "$systemd_root" + fi + + # Create init script + create_init_script "$ROOTFS_MOUNT_POINT" "$install_systemd" + + # Unmount filesystem + unmount_rootfs + + # Show final information + local size=$(get_file_size "$rootfs_image") + log_success "Root filesystem created: $rootfs_image ($size)" + + if [ "$install_systemd" = "true" ]; then + log_info "Systemd support: ENABLED" + else + log_info "Init system: Busybox" + fi + + end_timer +} + +# Quick root filesystem creation functions +create_initramfs_rootfs() { + local rootfs_image="${1:-boot/$DEFAULT_ROOTFS_NAME}" + create_rootfs "$rootfs_image" 50 false +} + +create_busybox_rootfs() { + local rootfs_image="${1:-boot/$DEFAULT_ROOTFS_NAME}" + local size_mb="${2:-150}" + create_rootfs "$rootfs_image" "$size_mb" false +} + +create_systemd_rootfs() { + local rootfs_image="${1:-boot/$DEFAULT_ROOTFS_NAME}" + local size_mb="${2:-200}" + create_rootfs "$rootfs_image" "$size_mb" true +} + +# Initialize rootfs utilities +init_rootfs() { + init_common + + # Check required commands + check_required_commands dd mkfs.ext4 mount umount sudo + + # Set up cleanup trap for mount point + setup_cleanup "$ROOTFS_MOUNT_POINT" + + log_debug "Root filesystem utilities initialized" +} + +# Export functions for use in other scripts +export -f create_rootfs_structure create_device_nodes create_system_files +export -f install_busybox install_systemd create_init_script +export -f mount_rootfs unmount_rootfs create_rootfs +export -f create_initramfs_rootfs create_busybox_rootfs create_systemd_rootfs +export -f init_rootfs \ No newline at end of file diff --git a/scripts/deploy-and-build-systemd-zvm.sh b/scripts/deploy-and-build-systemd-zvm.sh index d1b9b73..df34862 100755 --- a/scripts/deploy-and-build-systemd-zvm.sh +++ b/scripts/deploy-and-build-systemd-zvm.sh @@ -1,116 +1,52 @@ #!/bin/bash # Build systemd on z/VM using scp to transfer files +# SPDX-License-Identifier: GPL-2.0-only -set -e +# Source utilities +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/common.sh" +source "$SCRIPT_DIR/zvm-utils.sh" -# Colors -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' +# Initialize z/VM environment +init_zvm -# Load environment variables from .env file if available and variables not set -if [ -z "$ZVM_HOST" ] || [ -z "$ZVM_USER" ]; then - if [ -f ".env" ]; then - echo -e "${YELLOW}Loading z/VM configuration from .env file...${NC}" - set -a # Export all variables - source .env - set +a # Stop exporting - fi -fi +# Build systemd using the z/VM utilities +build_on_zvm "systemd" "scripts/build-systemd-zvm.sh" "systemd-minimal-s390x.tar.gz" 1800 -# Check if required variables are now set -if [ -z "$ZVM_HOST" ] || [ -z "$ZVM_USER" ]; then - echo -e "${RED}Error: ZVM_HOST and ZVM_USER environment variables must be set${NC}" - echo "" - echo "Option 1: Create a .env file (recommended):" - echo " cp .env.example .env" - echo " # Edit .env with your z/VM details" - echo "" - echo "Option 2: Export variables manually:" - echo " export ZVM_HOST=your.zvm.host" - echo " export ZVM_USER=your_username" - echo " export ZVM_SSH_KEY='your-ssh-key-content'" - exit 1 -fi - -ZVM_CONNECTION="${ZVM_USER}@${ZVM_HOST}" -ZVM_KEY="${ZVM_KEY:-zvm.pem}" - -echo -e "${GREEN}=== Building systemd on z/VM RHEL 9.6 ===${NC}" - -# Check if SSH key exists -if [ ! -f "$ZVM_KEY" ]; then - echo -e "${RED}Error: SSH key $ZVM_KEY not found${NC}" - exit 1 -fi - -# SSH options -SSH_OPTS="-i $ZVM_KEY -o StrictHostKeyChecking=${SSH_STRICT_HOST_KEY_CHECKING:-no} -o ConnectTimeout=${SSH_CONNECT_TIMEOUT:-10}" - -# 1. Copy build script to z/VM -echo -e "${YELLOW}1. Copying build script to z/VM...${NC}" -scp $SSH_OPTS scripts/build-systemd-zvm.sh $ZVM_CONNECTION:~/ - -# 2. Run the build script -echo -e "${YELLOW}2. Running systemd build on z/VM (this may take 10-15 minutes)...${NC}" -ssh $SSH_OPTS $ZVM_CONNECTION "chmod +x ~/build-systemd-zvm.sh && ./build-systemd-zvm.sh" - -# 3. Copy artifacts back with retry mechanism -echo -e "${YELLOW}3. Copying build artifacts back...${NC}" -mkdir -p output - -# Function to copy with retries -copy_with_retry() { - local max_retries=5 - local retry_count=0 - local success=0 - - while [ $retry_count -lt $max_retries ] && [ $success -eq 0 ]; do - if [ $retry_count -gt 0 ]; then - echo -e "${YELLOW}Retry $retry_count/$max_retries - waiting 10 seconds before retrying...${NC}" - sleep 10 - fi - - # Use timeout and compression options to handle stalls - if timeout 300 scp $SSH_OPTS -o ServerAliveInterval=15 -o ServerAliveCountMax=3 -C $ZVM_CONNECTION:~/systemd-minimal-s390x.tar.gz output/; then - success=1 - echo -e "${GREEN}✓ Transfer completed successfully${NC}" - else - retry_count=$((retry_count + 1)) - echo -e "${RED}Transfer failed or timed out${NC}" - - # Kill any stalled scp processes - pkill -f "scp.*systemd-minimal-s390x.tar.gz" 2>/dev/null || true - fi - done - - if [ $success -eq 0 ]; then - echo -e "${RED}Failed to transfer after $max_retries attempts${NC}" - echo -e "${YELLOW}You can manually copy the file with:${NC}" - echo "scp $SSH_OPTS $ZVM_CONNECTION:~/systemd-minimal-s390x.tar.gz output/" - exit 1 - fi -} - -# Perform the copy with retries -copy_with_retry - -# 4. Extract and organize -echo -e "${YELLOW}4. Extracting systemd...${NC}" +# Extract and organize systemd +print_section "Extracting systemd components" rm -rf output/systemd-root mkdir -p output/systemd-root tar xzf output/systemd-minimal-s390x.tar.gz -C output/systemd-root -# 5. Report results -echo -e "${GREEN}✓ Systemd built and extracted successfully!${NC}" -TOTAL_SIZE=$(du -sh output/systemd-root 2>/dev/null | cut -f1) -echo -e "${GREEN}Size: $TOTAL_SIZE${NC}" -echo -e "${GREEN}Location: output/systemd-root/${NC}" +# Report results +total_size=$(du -sh output/systemd-root 2>/dev/null | cut -f1) +log_success "Systemd built and extracted successfully!" +log_info "Size: $total_size" +log_info "Location: output/systemd-root/" # Show key files -echo -e "${YELLOW}Key systemd components:${NC}" -[ -f output/systemd-root/usr/lib/systemd/systemd ] && ls -lh output/systemd-root/usr/lib/systemd/systemd -[ -f output/systemd-root/usr/lib/systemd/systemd-journald ] && ls -lh output/systemd-root/usr/lib/systemd/systemd-journald -[ -f output/systemd-root/usr/lib/systemd/systemd-udevd ] && ls -lh output/systemd-root/usr/lib/systemd/systemd-udevd -[ -f output/systemd-root/usr/bin/systemctl ] && ls -lh output/systemd-root/usr/bin/systemctl \ No newline at end of file +print_section "Key systemd components" +if [ -f output/systemd-root/usr/lib/systemd/systemd ]; then + ls -lh output/systemd-root/usr/lib/systemd/systemd +else + log_warning "systemd binary not found" +fi + +if [ -f output/systemd-root/usr/lib/systemd/systemd-journald ]; then + ls -lh output/systemd-root/usr/lib/systemd/systemd-journald +else + log_warning "systemd-journald not found" +fi + +if [ -f output/systemd-root/usr/lib/systemd/systemd-udevd ]; then + ls -lh output/systemd-root/usr/lib/systemd/systemd-udevd +else + log_warning "systemd-udevd not found" +fi + +if [ -f output/systemd-root/usr/bin/systemctl ]; then + ls -lh output/systemd-root/usr/bin/systemctl +else + log_warning "systemctl not found" +fi \ No newline at end of file diff --git a/scripts/run-qemu-initramfs-only.sh b/scripts/run-qemu-initramfs-only.sh deleted file mode 100755 index 07d506e..0000000 --- a/scripts/run-qemu-initramfs-only.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -# Complete s390x Arch Linux system running from initramfs only - -# Get script directory and project root -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" - -echo "🚀 Starting Arch Linux s390x - Initramfs Only System" -echo "This will boot entirely from the initramfs (no separate root filesystem)" -echo "Press Ctrl-A X to exit" -echo "" - -cd "$PROJECT_ROOT" - -qemu-system-s390x \ - -machine s390-ccw-virtio \ - -cpu max \ - -m 2G \ - -kernel "boot/vmlinuz-linux" \ - -initrd "boot/initramfs-linux.img" \ - -append "console=ttyS0 rdinit=/init" \ - -nographic \ - -device virtio-net-ccw,netdev=net0 \ - -netdev user,id=net0 diff --git a/scripts/test-qemu-rootfs.sh b/scripts/test-qemu-rootfs.sh deleted file mode 100755 index 7837946..0000000 --- a/scripts/test-qemu-rootfs.sh +++ /dev/null @@ -1,192 +0,0 @@ -#!/bin/bash -# Test s390x Arch Linux with a root filesystem instead of just initramfs - -# Get script directory and project root -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" - -echo "🚀 Starting Arch Linux s390x - Root Filesystem Test" -echo "This will boot with initramfs then switch to a root filesystem" -echo "Press Ctrl-A X to exit" -echo "" - -cd "$PROJECT_ROOT" - -# Create a minimal root filesystem if it doesn't exist -ROOTFS_IMG="boot/rootfs-s390x.img" - -# Force recreation of root filesystem to get updated init script -echo "Creating minimal root filesystem (forcing recreation)..." -rm -f "$ROOTFS_IMG" - -# Create 150MB root filesystem image (for systemd) -dd if=/dev/zero of="$ROOTFS_IMG" bs=1M count=150 - -# Format as ext4 -mkfs.ext4 -F "$ROOTFS_IMG" - -# Mount and populate -mkdir -p /tmp/rootfs-mount -sudo mount -o loop "$ROOTFS_IMG" /tmp/rootfs-mount - -# Create basic directory structure -sudo mkdir -p /tmp/rootfs-mount/{bin,sbin,etc,proc,sys,dev,tmp,var,usr/{bin,sbin},lib,lib64} - -# Copy busybox as the main shell and utilities -sudo cp boot/busybox-s390x-static /tmp/rootfs-mount/bin/busybox - -# Create busybox symlinks for basic utilities -sudo chroot /tmp/rootfs-mount /bin/busybox --install -s - -# Copy systemd components if available -if [ -d "output/systemd-root" ]; then - echo "Installing systemd to root filesystem..." - sudo cp -r output/systemd-root/* /tmp/rootfs-mount/ - - # Create systemd init symlink - sudo ln -sf /usr/lib/systemd/systemd /tmp/rootfs-mount/sbin/init-systemd - - # Create systemd directories - sudo mkdir -p /tmp/rootfs-mount/run/systemd - sudo mkdir -p /tmp/rootfs-mount/var/log/journal - sudo mkdir -p /tmp/rootfs-mount/etc/systemd/system - - echo "✓ Systemd installed (42MB)" -else - echo "⚠ Systemd not found - using busybox init only" -fi - -# Pre-create TTY devices to avoid errors -sudo mknod /tmp/rootfs-mount/dev/console c 5 1 2>/dev/null || true -sudo mknod /tmp/rootfs-mount/dev/tty c 5 0 2>/dev/null || true -sudo mknod /tmp/rootfs-mount/dev/tty1 c 4 1 2>/dev/null || true -sudo mknod /tmp/rootfs-mount/dev/tty2 c 4 2 2>/dev/null || true -sudo mknod /tmp/rootfs-mount/dev/tty3 c 4 3 2>/dev/null || true -sudo mknod /tmp/rootfs-mount/dev/tty4 c 4 4 2>/dev/null || true - -# Create basic /etc/passwd -sudo tee /tmp/rootfs-mount/etc/passwd > /dev/null << 'EOF' -root:x:0:0:root:/root:/bin/sh -EOF - -# Create basic /etc/group -sudo tee /tmp/rootfs-mount/etc/group > /dev/null << 'EOF' -root:x:0: -EOF - -# Create /etc/os-release to identify as Arch Linux -sudo tee /tmp/rootfs-mount/etc/os-release > /dev/null << 'EOF' -NAME="Arch Linux" -PRETTY_NAME="Arch Linux" -ID=arch -BUILD_ID=rolling -ANSI_COLOR="38;2;23;147;209" -HOME_URL="https://archlinux.org/" -DOCUMENTATION_URL="https://wiki.archlinux.org/" -SUPPORT_URL="https://bbs.archlinux.org/" -BUG_REPORT_URL="https://bugs.archlinux.org/" -PRIVACY_POLICY_URL="https://terms.archlinux.org/docs/privacy-policy/" -LOGO=archlinux-logo -EOF - -# Create /etc/arch-release (traditional Arch identifier) -sudo tee /tmp/rootfs-mount/etc/arch-release > /dev/null << 'EOF' -Arch Linux s390x \r (\l) -EOF - -# Create /etc/hostname -sudo tee /tmp/rootfs-mount/etc/hostname > /dev/null << 'EOF' -archlinux-s390x -EOF - -# Create basic /etc/hosts -sudo tee /tmp/rootfs-mount/etc/hosts > /dev/null << 'EOF' -127.0.0.1 localhost -::1 localhost -127.0.1.1 archlinux-s390x.localdomain archlinux-s390x -EOF - -# Create basic init script for the root filesystem -sudo tee /tmp/rootfs-mount/sbin/init > /dev/null << 'EOF' -#!/bin/sh -echo "🎉 Welcome to Arch Linux s390x Root Filesystem!" -echo "This is running from a real root filesystem, not just initramfs." -echo "" - -# Mount essential filesystems -mount -t proc proc /proc 2>/dev/null || true -mount -t sysfs sysfs /sys 2>/dev/null || true -mount -t devtmpfs devtmpfs /dev 2>/dev/null || true - -# Create TTY devices that busybox init expects -mknod /dev/tty1 c 4 1 2>/dev/null || true -mknod /dev/tty2 c 4 2 2>/dev/null || true -mknod /dev/tty3 c 4 3 2>/dev/null || true -mknod /dev/tty4 c 4 4 2>/dev/null || true -mknod /dev/console c 5 1 2>/dev/null || true - -echo "=== ARCH LINUX VERIFICATION ===" -echo "Distribution info:" -if [ -f /etc/os-release ]; then - cat /etc/os-release -else - echo "No /etc/os-release found" -fi -echo "" - -if [ -f /etc/arch-release ]; then - echo "Arch release file:" - cat /etc/arch-release -else - echo "No /etc/arch-release found" -fi -echo "" - -echo "Hostname: $(cat /etc/hostname 2>/dev/null || echo 'Not set')" -echo "Architecture: $(uname -m)" -echo "Kernel: $(uname -r)" -echo "" - -echo "Root filesystem contents:" -ls -la / -echo "" - -echo "Available TTY devices:" -ls -la /dev/tty* 2>/dev/null || echo "No TTY devices found" -echo "" - -echo "Available commands:" -echo " ls, cat, mount, umount, ps, top, free, df, etc." -echo " Type 'busybox' to see all available applets" -echo "" - -echo "To verify this is Arch Linux, check:" -echo " cat /etc/os-release" -echo " cat /etc/arch-release" -echo " uname -a" -echo "" - -# Start a shell -exec /bin/sh -EOF - -sudo chmod +x /tmp/rootfs-mount/sbin/init - -sudo umount /tmp/rootfs-mount -rmdir /tmp/rootfs-mount - -echo "✅ Root filesystem created at $ROOTFS_IMG" - -# Boot with root filesystem -qemu-system-s390x \ - -machine s390-ccw-virtio \ - -cpu max \ - -m 2G \ - -kernel "boot/vmlinuz-linux" \ - -initrd "boot/initramfs-linux.img" \ - -append "console=ttyS0 root=/dev/vda rw init=/sbin/init" \ - -drive file="$ROOTFS_IMG",format=raw,if=none,id=rootdisk \ - -device virtio-blk-ccw,drive=rootdisk \ - -nographic \ - -device virtio-net-ccw,netdev=net0 \ - -netdev user,id=net0 \ No newline at end of file diff --git a/scripts/test-qemu-systemd.sh b/scripts/test-qemu-systemd.sh deleted file mode 100755 index 8f4a567..0000000 --- a/scripts/test-qemu-systemd.sh +++ /dev/null @@ -1,102 +0,0 @@ -#!/bin/bash -# Test s390x Arch Linux with systemd as init - -# Get script directory and project root -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" - -echo "🚀 Starting Arch Linux s390x - Systemd Test" -echo "This will boot with systemd as PID 1" -echo "Press Ctrl-A X to exit" -echo "" - -cd "$PROJECT_ROOT" - -# Check if systemd is available -if [ ! -d "output/systemd-root" ]; then - echo "Error: Systemd not found. Run 'make systemd' first" - exit 1 -fi - -# Create systemd-enabled root filesystem directly -echo "Preparing systemd-enabled root filesystem..." - -ROOTFS_IMG="boot/rootfs-s390x.img" - -# Force recreation of root filesystem -rm -f "$ROOTFS_IMG" - -# Create 150MB root filesystem image (for systemd) -dd if=/dev/zero of="$ROOTFS_IMG" bs=1M count=150 - -# Format as ext4 -mkfs.ext4 -F "$ROOTFS_IMG" - -# Mount and populate -mkdir -p /tmp/rootfs-mount -sudo mount -o loop "$ROOTFS_IMG" /tmp/rootfs-mount - -# Create basic directory structure -sudo mkdir -p /tmp/rootfs-mount/{bin,sbin,etc,proc,sys,dev,tmp,var,usr/{bin,sbin},lib,lib64} - -# Copy busybox as fallback -sudo cp boot/busybox-s390x-static /tmp/rootfs-mount/bin/busybox -sudo chroot /tmp/rootfs-mount /bin/busybox --install -s - -# Install systemd -echo "Installing systemd to root filesystem..." -sudo cp -r output/systemd-root/* /tmp/rootfs-mount/ - -# Create systemd directories -sudo mkdir -p /tmp/rootfs-mount/run/systemd -sudo mkdir -p /tmp/rootfs-mount/var/log/journal -sudo mkdir -p /tmp/rootfs-mount/etc/systemd/system -sudo mkdir -p /tmp/rootfs-mount/sys -sudo mkdir -p /tmp/rootfs-mount/proc - -# Create device nodes -sudo mknod /tmp/rootfs-mount/dev/console c 5 1 2>/dev/null || true -sudo mknod /tmp/rootfs-mount/dev/tty c 5 0 2>/dev/null || true -sudo mknod /tmp/rootfs-mount/dev/tty1 c 4 1 2>/dev/null || true - -# Create basic files -sudo tee /tmp/rootfs-mount/etc/passwd > /dev/null << 'EOF' -root:x:0:0:root:/root:/bin/sh -EOF - -sudo tee /tmp/rootfs-mount/etc/group > /dev/null << 'EOF' -root:x:0: -EOF - -sudo tee /tmp/rootfs-mount/etc/os-release > /dev/null << 'EOF' -NAME="Arch Linux" -PRETTY_NAME="Arch Linux" -ID=arch -BUILD_ID=rolling -EOF - -sudo tee /tmp/rootfs-mount/etc/hostname > /dev/null << 'EOF' -archlinux-s390x -EOF - -# Unmount -sudo umount /tmp/rootfs-mount -rmdir /tmp/rootfs-mount - -echo "✓ Root filesystem with systemd prepared" - -# Run QEMU with systemd as init -echo "Booting with systemd..." -qemu-system-s390x \ - -M s390-ccw-virtio \ - -cpu qemu \ - -m 2G \ - -smp 1 \ - -kernel boot/vmlinuz-linux \ - -initrd boot/initramfs-linux.img \ - -append "console=ttyS0 root=/dev/vda rw init=/usr/lib/systemd/systemd systemd.log_level=debug" \ - -drive file=boot/rootfs-s390x.img,format=raw,if=virtio \ - -netdev user,id=network0 \ - -device virtio-net,netdev=network0 \ - -nographic \ - -serial mon:stdio \ No newline at end of file diff --git a/scripts/test-qemu.sh b/scripts/test-qemu.sh new file mode 100755 index 0000000..82381bd --- /dev/null +++ b/scripts/test-qemu.sh @@ -0,0 +1,174 @@ +#!/bin/bash +# Unified QEMU testing script for Arch Linux s390x +# SPDX-License-Identifier: GPL-2.0-only + +# Source utilities +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/common.sh" +source "$SCRIPT_DIR/create-rootfs.sh" + +# Test configuration +TEST_MODE="${1:-initramfs}" +ROOTFS_SIZE="${2:-150}" + +# Initialize +init_common + +# Change to project root +cd "$PROJECT_ROOT" + +# Validate boot files exist +check_required_files "boot/vmlinuz-linux" "boot/initramfs-linux.img" + +# Test functions +test_initramfs_only() { + print_header "Testing Arch Linux s390x - Initramfs Only" + + log_info "Starting QEMU with initramfs-only boot..." + log_info "This will boot into the initramfs environment with busybox shell" + log_info "Press Ctrl-A X to exit QEMU" + echo + + run_qemu_s390x "rdinit=/init" +} + +test_rootfs_boot() { + print_header "Testing Arch Linux s390x - Root Filesystem" + + local rootfs_img="boot/rootfs-s390x.img" + + # Create root filesystem if it doesn't exist or is older than busybox + if [ ! -f "$rootfs_img" ] || [ "boot/busybox-s390x-static" -nt "$rootfs_img" ]; then + log_info "Creating root filesystem..." + create_busybox_rootfs "$rootfs_img" "$ROOTFS_SIZE" + else + log_info "Using existing root filesystem: $rootfs_img" + fi + + log_info "Starting QEMU with root filesystem switching..." + log_info "This will boot from initramfs, then switch to root filesystem" + log_info "Expected: Arch Linux identification and busybox shell on root filesystem" + log_info "Press Ctrl-A X to exit QEMU" + echo + + run_qemu_s390x "root=/dev/vda rw init=/sbin/init" \ + "-drive file=$rootfs_img,format=raw,if=none,id=rootdisk -device virtio-blk-ccw,drive=rootdisk" +} + +test_systemd_boot() { + print_header "Testing Arch Linux s390x - Systemd" + + local rootfs_img="boot/rootfs-s390x.img" + + # Check if systemd is available + if [ ! -d "output/systemd-root" ]; then + die "Systemd not found. Run 'make systemd' first to build systemd components" + fi + + # Create systemd root filesystem + log_info "Creating systemd root filesystem..." + create_systemd_rootfs "$rootfs_img" "$ROOTFS_SIZE" + + log_info "Starting QEMU with systemd as init..." + log_info "This will boot with systemd as PID 1" + log_info "Expected: 'Welcome to Arch Linux!' and systemd boot sequence" + log_info "Press Ctrl-A X to exit QEMU" + echo + + run_qemu_s390x "root=/dev/vda rw init=/usr/lib/systemd/systemd systemd.log_level=debug" \ + "-drive file=$rootfs_img,format=raw,if=virtio -serial mon:stdio" +} + +test_qemu_info() { + print_header "QEMU System Information" + + log_info "QEMU s390x version:" + qemu-system-s390x --version || log_warning "QEMU s390x not found" + + echo + log_info "Available s390x machines:" + qemu-system-s390x -machine help | grep s390 || true + + echo + log_info "Boot files status:" + if [ -f "boot/vmlinuz-linux" ]; then + local kernel_size=$(get_file_size "boot/vmlinuz-linux") + log_success "Kernel: boot/vmlinuz-linux ($kernel_size)" + else + log_error "Kernel: boot/vmlinuz-linux (missing)" + fi + + if [ -f "boot/initramfs-linux.img" ]; then + local initramfs_size=$(get_file_size "boot/initramfs-linux.img") + log_success "Initramfs: boot/initramfs-linux.img ($initramfs_size)" + else + log_error "Initramfs: boot/initramfs-linux.img (missing)" + fi + + if [ -f "boot/rootfs-s390x.img" ]; then + local rootfs_size=$(get_file_size "boot/rootfs-s390x.img") + log_success "Root filesystem: boot/rootfs-s390x.img ($rootfs_size)" + else + log_info "Root filesystem: boot/rootfs-s390x.img (will be created)" + fi + + echo + log_info "Systemd availability:" + if [ -d "output/systemd-root" ]; then + log_success "Systemd: Available in output/systemd-root" + else + log_warning "Systemd: Not available (run 'make systemd' to build)" + fi +} + +show_usage() { + cat << EOF +Usage: $0 [MODE] [ROOTFS_SIZE] + +MODES: + initramfs Test initramfs-only system (default) + rootfs Test with root filesystem switching + systemd Test with systemd as init system + info Show system information and file status + +ROOTFS_SIZE: + Size in MB for root filesystem creation (default: 150) + +Examples: + $0 # Test initramfs-only (default) + $0 rootfs # Test root filesystem with default size + $0 systemd 200 # Test systemd with 200MB root filesystem + $0 info # Show system information + +Notes: + - All tests require boot/vmlinuz-linux and boot/initramfs-linux.img + - Systemd test requires 'make systemd' to be run first + - Root filesystem will be created automatically if needed + - Press Ctrl-A X to exit QEMU during testing +EOF +} + +# Main execution +case "$TEST_MODE" in + "initramfs"|"initramfs-only") + test_initramfs_only + ;; + "rootfs"|"root"|"filesystem") + test_rootfs_boot + ;; + "systemd"|"systemd-boot") + test_systemd_boot + ;; + "info"|"status") + test_qemu_info + ;; + "help"|"-h"|"--help") + show_usage + ;; + *) + log_error "Unknown test mode: $TEST_MODE" + echo + show_usage + exit 1 + ;; +esac \ No newline at end of file diff --git a/scripts/zvm-utils.sh b/scripts/zvm-utils.sh new file mode 100644 index 0000000..40c6777 --- /dev/null +++ b/scripts/zvm-utils.sh @@ -0,0 +1,260 @@ +#!/bin/bash +# z/VM connection and build utilities for Arch Linux s390x +# SPDX-License-Identifier: GPL-2.0-only + +# Source common utilities +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/common.sh" + +# z/VM Configuration defaults +export ZVM_KEY="${ZVM_KEY:-zvm.pem}" +export SSH_CONNECT_TIMEOUT="${SSH_CONNECT_TIMEOUT:-10}" +export SSH_STRICT_HOST_KEY_CHECKING="${SSH_STRICT_HOST_KEY_CHECKING:-no}" +export SSH_SERVER_ALIVE_INTERVAL="${SSH_SERVER_ALIVE_INTERVAL:-15}" +export SSH_SERVER_ALIVE_COUNT_MAX="${SSH_SERVER_ALIVE_COUNT_MAX:-3}" +export SCP_TIMEOUT="${SCP_TIMEOUT:-300}" +export RETRY_MAX_ATTEMPTS="${RETRY_MAX_ATTEMPTS:-5}" +export RETRY_DELAY="${RETRY_DELAY:-10}" + +# Load z/VM environment variables +load_zvm_env() { + log_debug "Loading z/VM environment..." + + # Try to load from .env if variables aren't set + if [ -z "${ZVM_HOST:-}" ] || [ -z "${ZVM_USER:-}" ]; then + if [ -f ".env" ]; then + log_info "Loading z/VM configuration from .env file..." + load_env ".env" + fi + fi + + # Validate required variables + if [ -z "${ZVM_HOST:-}" ]; then + die "ZVM_HOST environment variable must be set" + fi + + if [ -z "${ZVM_USER:-}" ]; then + die "ZVM_USER environment variable must be set" + fi + + # Set up derived variables + export ZVM_CONNECTION="${ZVM_USER}@${ZVM_HOST}" + export SSH_OPTS="-i $ZVM_KEY -o StrictHostKeyChecking=$SSH_STRICT_HOST_KEY_CHECKING -o ConnectTimeout=$SSH_CONNECT_TIMEOUT -o ServerAliveInterval=$SSH_SERVER_ALIVE_INTERVAL -o ServerAliveCountMax=$SSH_SERVER_ALIVE_COUNT_MAX" + + log_debug "z/VM connection: $ZVM_CONNECTION" + log_debug "SSH options: $SSH_OPTS" +} + +# Setup SSH key for z/VM access +setup_zvm_ssh_key() { + if [ -n "${ZVM_SSH_KEY:-}" ] && [ ! -f "$ZVM_KEY" ]; then + log_info "Setting up SSH key for z/VM access..." + echo "$ZVM_SSH_KEY" > "$ZVM_KEY" + chmod 600 "$ZVM_KEY" + log_success "SSH key created: $ZVM_KEY" + elif [ ! -f "$ZVM_KEY" ]; then + die "SSH key not found: $ZVM_KEY. Set ZVM_SSH_KEY environment variable or create the key file." + fi +} + +# Test z/VM connection +test_zvm_connection() { + log_info "Testing z/VM connection..." + + if ssh $SSH_OPTS "$ZVM_CONNECTION" "echo 'Connection successful'" >/dev/null 2>&1; then + log_success "z/VM connection test passed" + return 0 + else + log_error "z/VM connection test failed" + log_error "Check your SSH key, hostname, and network connectivity" + return 1 + fi +} + +# Copy file to z/VM with retry mechanism +copy_to_zvm() { + local source_file="$1" + local dest_path="${2:-~/}" + local max_retries="${3:-$RETRY_MAX_ATTEMPTS}" + + log_info "Copying $source_file to z/VM:$dest_path" + + if [ ! -f "$source_file" ]; then + die "Source file not found: $source_file" + fi + + local retry_count=0 + local success=0 + + while [ $retry_count -lt $max_retries ] && [ $success -eq 0 ]; do + if [ $retry_count -gt 0 ]; then + log_warning "Retry $retry_count/$max_retries - waiting ${RETRY_DELAY} seconds..." + sleep "$RETRY_DELAY" + fi + + log_debug "Attempt $((retry_count + 1)): scp $source_file to $ZVM_CONNECTION:$dest_path" + + # Use timeout and compression options to handle stalls + if timeout "$SCP_TIMEOUT" scp $SSH_OPTS -C "$source_file" "$ZVM_CONNECTION:$dest_path"; then + success=1 + log_success "Transfer completed successfully" + else + retry_count=$((retry_count + 1)) + log_error "Transfer failed or timed out" + + # Kill any stalled scp processes + pkill -f "scp.*$(basename "$source_file")" 2>/dev/null || true + fi + done + + if [ $success -eq 0 ]; then + die "Failed to transfer $source_file after $max_retries attempts" + fi +} + +# Copy file from z/VM with retry mechanism +copy_from_zvm() { + local source_path="$1" + local dest_file="$2" + local max_retries="${3:-$RETRY_MAX_ATTEMPTS}" + + log_info "Copying z/VM:$source_path to $dest_file" + + local retry_count=0 + local success=0 + + while [ $retry_count -lt $max_retries ] && [ $success -eq 0 ]; do + if [ $retry_count -gt 0 ]; then + log_warning "Retry $retry_count/$max_retries - waiting ${RETRY_DELAY} seconds..." + sleep "$RETRY_DELAY" + fi + + log_debug "Attempt $((retry_count + 1)): scp from $ZVM_CONNECTION:$source_path" + + # Use timeout and compression options to handle stalls + if timeout "$SCP_TIMEOUT" scp $SSH_OPTS -C "$ZVM_CONNECTION:$source_path" "$dest_file"; then + success=1 + log_success "Transfer completed successfully" + else + retry_count=$((retry_count + 1)) + log_error "Transfer failed or timed out" + + # Kill any stalled scp processes + pkill -f "scp.*$(basename "$source_path")" 2>/dev/null || true + fi + done + + if [ $success -eq 0 ]; then + die "Failed to transfer $source_path after $max_retries attempts" + fi +} + +# Execute command on z/VM +execute_on_zvm() { + local command="$1" + local timeout_duration="${2:-600}" + + log_info "Executing on z/VM: $command" + + if timeout "$timeout_duration" ssh $SSH_OPTS "$ZVM_CONNECTION" "$command"; then + log_success "Command executed successfully" + return 0 + else + log_error "Command failed or timed out: $command" + return 1 + fi +} + +# Execute script on z/VM +execute_script_on_zvm() { + local script_path="$1" + local remote_path="${2:-~/$(basename "$script_path")}" + local timeout_duration="${3:-600}" + + log_info "Executing script $script_path on z/VM" + + # Copy script to z/VM + copy_to_zvm "$script_path" "$remote_path" + + # Make it executable and run it + execute_on_zvm "chmod +x $remote_path && $remote_path" "$timeout_duration" +} + +# Build component on z/VM using a build script +build_on_zvm() { + local component_name="$1" + local build_script="$2" + local output_archive="${3:-${component_name}-s390x.tar.gz}" + local timeout_duration="${4:-1800}" + + print_section "Building $component_name on z/VM" + + start_timer + + # Test connection first + test_zvm_connection || die "Cannot connect to z/VM" + + # Execute build script + log_info "Running build script: $build_script" + execute_script_on_zvm "$build_script" "~/build-${component_name}.sh" "$timeout_duration" + + # Copy back the results + if [ -n "$output_archive" ]; then + log_info "Copying build artifacts..." + ensure_dir "$OUTPUT_DIR" + copy_from_zvm "~/$output_archive" "$OUTPUT_DIR/$output_archive" + + if [ -f "$OUTPUT_DIR/$output_archive" ]; then + local size=$(get_file_size "$OUTPUT_DIR/$output_archive") + log_success "$component_name build completed - $size archive created" + else + die "Build artifact not found: $OUTPUT_DIR/$output_archive" + fi + fi + + end_timer +} + +# Initialize z/VM utilities +init_zvm() { + # Initialize common environment first + init_common + + # Load z/VM specific environment + load_zvm_env + + # Setup SSH key + setup_zvm_ssh_key + + log_debug "z/VM utilities initialized" +} + +# Clean up z/VM build artifacts +cleanup_zvm_build() { + local component_name="$1" + local artifacts="${2:-${component_name}-s390x.tar.gz build-${component_name}.sh}" + + log_info "Cleaning up z/VM build artifacts for $component_name" + + for artifact in $artifacts; do + execute_on_zvm "rm -f ~/$artifact" || log_warning "Failed to remove $artifact" + done +} + +# Show z/VM system information +show_zvm_info() { + log_info "Gathering z/VM system information..." + + echo + echo -e "${CYAN}=== z/VM System Information ===${NC}" + execute_on_zvm "uname -a" || true + execute_on_zvm "cat /etc/os-release" || true + execute_on_zvm "df -h ~" || true + execute_on_zvm "free -h" || true + echo +} + +# Export functions for use in other scripts +export -f load_zvm_env setup_zvm_ssh_key test_zvm_connection +export -f copy_to_zvm copy_from_zvm execute_on_zvm execute_script_on_zvm +export -f build_on_zvm init_zvm cleanup_zvm_build show_zvm_info \ No newline at end of file