cleaned up scripts to be more DRY
This commit is contained in:
parent
0e3a4c4dd5
commit
8032b83e6a
15 changed files with 1378 additions and 554 deletions
11
.github/workflows/release.yml
vendored
11
.github/workflows/release.yml
vendored
|
|
@ -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
8
.gitignore
vendored
|
|
@ -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
|
||||
|
|
|
|||
29
Makefile
29
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):
|
||||
|
|
|
|||
|
|
@ -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.
|
|
@ -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.
|
||||
|
|
@ -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
286
scripts/common.sh
Normal 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
581
scripts/create-rootfs.sh
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
174
scripts/test-qemu.sh
Executable 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
260
scripts/zvm-utils.sh
Normal 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
|
||||
Loading…
Reference in a new issue