cleaned up scripts to be more DRY

This commit is contained in:
Josephine Pfeiffer 2025-06-05 13:03:58 +02:00
parent 0e3a4c4dd5
commit 8032b83e6a
No known key found for this signature in database
15 changed files with 1378 additions and 554 deletions

View file

@ -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

8
.gitignore vendored
View file

@ -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

View file

@ -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):

View file

@ -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:

Binary file not shown.

View file

@ -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.

View file

@ -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

286
scripts/common.sh Normal file
View file

@ -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

581
scripts/create-rootfs.sh Normal file
View file

@ -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.
# <file system> <dir> <type> <options> <dump> <pass>
/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

View file

@ -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
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

View file

@ -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

View file

@ -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

View file

@ -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

174
scripts/test-qemu.sh Executable file
View file

@ -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

260
scripts/zvm-utils.sh Normal file
View file

@ -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