fix: boot process improvements — dependency cycle, INIT_NOTIFY, probing loop, and log spam fixes

- Fix P15-8-init-cycle-detection.patch: replace visiting+error with seen+silent-skip
  to eliminate 11 false-positive 'dependency cycle detected' errors on shared deps
- Fix P0-daemon-fix-init-notify-unwrap.patch: remove eprintln! for missing
  INIT_NOTIFY (expected for oneshot_async services, ~7 daemons affected)
- Fix driver-manager hotplug loop: add PERMANENTLY_SKIPPED static set shared
  between hotplug handler and DriverConfig::probe() to stop infinite re-probing
  of Fatal/NotSupported/deferred-exhausted device+driver pairs (e.g. ided)
- Fix driver-manager log_timeline: suppress repeated EPIPE/ENOENT errors with
  AtomicI32 dedup and AtomicBool one-shot guards for boot timeline JSON
- Add driver-manager SIGTERM handler, ACPI bus registration, --status mode,
  driver reap loop, graceful shutdown, and reduced deferred retries (30→3)
This commit is contained in:
2026-05-17 12:34:02 +03:00
parent 7914626765
commit cee25393d8
4002 changed files with 574970 additions and 1680003 deletions
+97
View File
@@ -0,0 +1,97 @@
#!/bin/bash
# gnulib-cross-fix.sh — Fix gnulib cross-compilation misdetections in config.h
#
# Usage: gnulib-cross-fix.sh <path/to/lib/config.h>
#
# gnulib's configure can't run test programs during cross-compilation for
# x86_64-unknown-redox (relibc target). It assumes system headers/types are
# missing and generates broken fallback #defines and replacement headers.
#
# This script patches the generated config.h to correct those misdetections,
# telling gnulib that relibc provides standard POSIX headers and types.
#
# Call this AFTER configure but BEFORE make in any gnulib-based recipe:
# "${COOKBOOK_CONFIGURE}" "${COOKBOOK_CONFIGURE_FLAGS[@]}"
# gnulib-cross-fix.sh "${COOKBOOK_BUILD}/lib/config.h"
# "${COOKBOOK_MAKE}" -j "${COOKBOOK_MAKE_JOBS}"
set -euo pipefail
CONFIG_H="${1:?Usage: gnulib-cross-fix.sh <path/to/config.h>}"
if [ ! -f "$CONFIG_H" ]; then
echo "gnulib-cross-fix: $CONFIG_H not found, skipping" >&2
exit 0
fi
echo "gnulib-cross-fix: patching $CONFIG_H for relibc cross-compilation"
# Comment out broken type fallbacks (relibc provides these correctly)
perl -pi -e 's,^#define gid_t int\b,/* #undef gid_t -- relibc provides */,' "$CONFIG_H"
perl -pi -e 's,^#define uid_t int\b,/* #undef uid_t -- relibc provides */,' "$CONFIG_H"
perl -pi -e 's,^#define intmax_t long long\b,/* #undef intmax_t -- relibc provides */,' "$CONFIG_H"
perl -pi -e 's,^#define ssize_t int\b,/* #undef ssize_t -- relibc provides */,' "$CONFIG_H"
perl -pi -e 's,^#define ptrdiff_t long\b,/* #undef ptrdiff_t -- relibc provides */,' "$CONFIG_H"
perl -pi -e 's,^#define nlink_t int\b,/* #undef nlink_t -- relibc provides */,' "$CONFIG_H"
perl -pi -e 's,^#define mbstate_t int\b,/* #undef mbstate_t -- relibc provides */,' "$CONFIG_H"
# Force HAVE_ macros for standard headers (gnulib can't detect these during cross-compilation)
for macro in \
HAVE_INTTYPES_H \
HAVE_INTTYPES_H_WITH_UINTMAX \
HAVE_INTMAX_T \
HAVE_WCHAR_H \
HAVE_WCTYPE_H \
HAVE_STDLIB_H \
HAVE_SPAWN_H \
HAVE_POSIX_SPAWNATTR_T \
HAVE_POSIX_SPAWN_FILE_ACTIONS_T \
HAVE_SIGNED_WCHAR_T \
HAVE_SIGSET_T \
HAVE_SIGACTION \
HAVE_SIGADDSET \
HAVE_SIGDELSET \
HAVE_SIGEMPTYSET \
HAVE_SIGFILLSET \
HAVE_SIGISMEMBER \
HAVE_SIGPENDING \
HAVE_SIGPROCMASK \
HAVE_SIGSUSPEND \
HAVE_BTOWC \
HAVE_MBRTOWC \
HAVE_MBSINIT \
HAVE_WCRTOMB \
HAVE_WCTOB \
HAVE_WCWIDTH \
HAVE_MBSRTOWCS \
HAVE_WCSWIDTH \
HAVE___FSETERR \
HAVE_GETLOCALENAME_L \
; do
perl -pi -e "s,/\\* #undef ${macro} \\*/,#define ${macro} 1," "$CONFIG_H" || true
done
# Also patch generated replacement headers to disable rpl_ function renames.
# gnulib generates lib/wchar.h, lib/signal.h, lib/stdio.h etc. with #define func rpl_func
# when REPLACE_* is 1. Since relibc provides these functions, we need to undo the renames.
LIB_DIR="$(dirname "$CONFIG_H")"
for hdr in "$LIB_DIR"/wchar.h "$LIB_DIR"/signal.h "$LIB_DIR"/stdio.h "$LIB_DIR"/stdlib.h "$LIB_DIR"/string.h; do
if [ -f "$hdr" ]; then
# Replace rpl_ function names with the originals in #define lines
# e.g. #define btowc rpl_btowc → /* #define btowc rpl_btowc -- relibc provides */
perl -pi -e 's,^#(\s*)define (\w+) rpl_\2,/* #$1define $2 rpl_$2 -- relibc provides */,' "$hdr" || true
fi
done
# NOTE: We do NOT replace #include_next directives. The cross-compiler (Clang)
# supports #include_next natively and the sysroot include path is set up correctly
# so that #include_next skips the build directory and finds relibc's system headers.
#
# Previous versions of this script replaced #include_next <X.h> with #include <X.h>,
# but this caused self-recursion in newer gnulib (m4-1.14+, flex) that removed the
# _GL_ALREADY_INCLUDING_WCHAR_H guard. The split double-inclusion guard pattern
# (_GL_WCHAR_H etc.) prevents re-entry into the normal section, but the special
# invocation section (for __need_mbstate_t/__need_wint_t) is unguarded and would
# recurse if #include found the gnulib wrapper instead of the system header.
echo "gnulib-cross-fix: done"
+214
View File
@@ -0,0 +1,214 @@
#!/usr/bin/env bash
# rebuild-cascade.sh — Rebuild a package and all packages that depend on it
#
# Usage:
# ./local/scripts/rebuild-cascade.sh <package> [package2 ...]
# ./local/scripts/rebuild-cascade.sh --dry-run <package>
#
# When a low-level package (e.g. relibc) changes, every package that
# transitively depends on it must be rebuilt. This script:
# 1. Identifies all packages that depend on the target(s)
# 2. Cooks the target package(s) first
# 3. Cooks all dependents in dependency order
# 4. Pushes all rebuilt packages to the sysroot
#
# A package is considered a dependent if its recipe.toml lists the target
# in its [package].dependencies array, OR if its recipe.toml has
# dependencies = [...] that includes the target.
#
# Exit codes:
# 0 — all packages rebuilt and pushed successfully
# 1 — one or more packages failed to build
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "${SCRIPT_DIR}/../.." && pwd)"
REPO_BIN="${ROOT_DIR}/target/release/repo"
DRY_RUN=0
PACKAGES=()
for arg in "$@"; do
case "$arg" in
--dry-run|-n)
DRY_RUN=1
;;
--help|-h)
echo "Usage: $0 [--dry-run] <package> [package2 ...]"
echo ""
echo "Rebuilds the named package(s) and all packages that transitively"
echo "depend on them, then pushes all to the sysroot."
exit 0
;;
-*)
echo "Unknown option: $arg" >&2
exit 1
;;
*)
PACKAGES+=("$arg")
;;
esac
done
if [ ${#PACKAGES[@]} -eq 0 ]; then
echo "Usage: $0 [--dry-run] <package> [package2 ...]" >&2
exit 1
fi
# Build the repo binary if needed
if [ ! -x "${REPO_BIN}" ]; then
echo "Building repo binary..."
(cd "${ROOT_DIR}" && cargo build --release --bin repo)
fi
cd "${ROOT_DIR}"
# Collect all recipe directories
RECIPE_DIRS=()
for pkg in "${PACKAGES[@]}"; do
# Find the recipe directory
found=0
for dir in recipes/*/; do
if [ -d "${dir}${pkg}" ]; then
RECIPE_DIRS+=("${dir}${pkg}")
found=1
break
fi
done
# Also check local/recipes
if [ $found -eq 0 ]; then
for dir in local/recipes/*/; do
if [ -d "${dir}${pkg}" ]; then
RECIPE_DIRS+=("${dir}${pkg}")
found=1
break
fi
done
fi
if [ $found -eq 0 ]; then
echo "ERROR: recipe not found for package '${pkg}'" >&2
exit 1
fi
done
# Find all recipes that depend on any of the target packages
# by scanning their recipe.toml for dependency entries
find_reverse_deps() {
local target="$1"
local result=()
# Search all recipe.toml files for dependencies
while IFS= read -r -d '' recipe_toml; do
# Get the package name from the directory
pkg_dir="$(dirname "${recipe_toml}")"
pkg_name="$(basename "${pkg_dir}")"
# Skip the target itself
if [ "${pkg_name}" = "${target}" ]; then
continue
fi
# Check if this recipe depends on the target
if grep -q "dependencies.*=.*\[.*${target}.*\]" "${recipe_toml}" 2>/dev/null; then
result+=("${pkg_name}")
fi
done < <(find recipes/ local/recipes/ -name "recipe.toml" -print0 2>/dev/null)
printf '%s\n' "${result[@]}" | sort -u
}
# Build a complete cascade set using BFS
echo "=== Analyzing dependency cascade ==="
CASCADE=()
VISITED=()
QUEUE=("${PACKAGES[@]}")
while [ ${#QUEUE[@]} -gt 0 ]; do
current="${QUEUE[0]}"
QUEUE=("${QUEUE[@]:1}")
# Skip if already visited
for v in "${VISITED[@]}"; do
if [ "$v" = "$current" ]; then
continue 2
fi
done
VISITED+=("$current")
# Find packages that depend on current
mapfile -t rdeps < <(find_reverse_deps "$current" 2>/dev/null || true)
for dep in "${rdeps[@]}"; do
CASCADE+=("${dep}")
QUEUE+=("${dep}")
done
done
# Remove duplicates and sort
UNIQUE_CASCADE=($(printf '%s\n' "${CASCADE[@]}" | sort -u))
# Build order: cook the target packages first, then dependents
BUILD_ORDER=("${PACKAGES[@]}" "${UNIQUE_CASCADE[@]}")
TOTAL=${#BUILD_ORDER[@]}
echo ""
echo "=== Cascade rebuild plan (${TOTAL} packages) ==="
for i in "${!BUILD_ORDER[@]}"; do
pkg="${BUILD_ORDER[$i]}"
if printf '%s\n' "${PACKAGES[@]}" | grep -q "^${pkg}$"; then
echo " [$((i+1))/${TOTAL}] ${pkg} (ROOT)"
else
echo " [$((i+1))/${TOTAL}] ${pkg} (dependent)"
fi
done
if [ $DRY_RUN -eq 1 ]; then
echo ""
echo "=== Dry run — no packages will be built ==="
exit 0
fi
echo ""
echo "=== Starting cascade rebuild ==="
FAILED=()
BUILT=()
export REDOXER_TOOLCHAIN="${ROOT_DIR}/prefix/x86_64-unknown-redox/relibc-install"
for i in "${!BUILD_ORDER[@]}"; do
pkg="${BUILD_ORDER[$i]}"
echo ""
echo "--- [$((i+1))/${TOTAL}] Building ${pkg} ---"
if "${REPO_BIN}" cook "${pkg}"; then
BUILT+=("${pkg}")
echo "--- ${pkg} built successfully ---"
else
FAILED+=("${pkg}")
echo "ERROR: ${pkg} failed to build" >&2
echo "Continuing with remaining packages..."
fi
done
echo ""
echo "=== Pushing ${#BUILT[@]} built packages to sysroot ==="
for pkg in "${BUILT[@]}"; do
echo "Pushing ${pkg}..."
"${REPO_BIN}" push "${pkg}" || echo "WARNING: push ${pkg} failed"
done
echo ""
echo "=== Cascade rebuild summary ==="
echo " Built: ${#BUILT[@]}/${TOTAL}"
echo " Failed: ${#FAILED[@]}/${TOTAL}"
if [ ${#FAILED[@]} -gt 0 ]; then
echo " Failed packages:"
for pkg in "${FAILED[@]}"; do
echo " - ${pkg}"
done
exit 1
fi
echo " All packages rebuilt and pushed successfully."
exit 0
+255
View File
@@ -0,0 +1,255 @@
#!/usr/bin/env bash
# P19-1: Multi-Core Driver Stress Test for Red Bear OS
#
# Validates SMP stability under parallel I/O load:
# - 4 CPU cores exercising scheduler (P17)
# - Parallel disk I/O via dd (filesystem scheme)
# - Parallel scheme reads (IPC paths)
# - Concurrent process creation/teardown
# - Panic/hang detection
#
# Usage:
# ./local/scripts/test-smp-stress-qemu.sh [--check]
# ./local/scripts/test-smp-stress-qemu.sh --duration 60
#
# Options:
# --check Run full QEMU test (default)
# --duration N Stress duration in seconds (default: 30)
# --smp N Number of CPU cores (default: 4)
# --help Show this help
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Defaults
DURATION=30
SMP=4
ACTION="check"
IMAGE=""
FIRMWARE="/usr/share/edk2/x64/OVMF_CODE.4m.fd"
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--check) ACTION="check"; shift ;;
--duration) DURATION="$2"; shift 2 ;;
--smp) SMP="$2"; shift 2 ;;
--image) IMAGE="$2"; shift 2 ;;
--help)
head -20 "$0" | grep '^#' | sed 's/^# \?//'
exit 0
;;
*) echo "Unknown argument: $1"; exit 1 ;;
esac
done
# Resolve image
if [[ -z "$IMAGE" ]]; then
IMAGE="$PROJECT_DIR/build/x86_64/redbear-mini.iso"
fi
if [[ ! -f "$IMAGE" ]]; then
echo "ERROR: Image not found: $IMAGE"
echo "Run: make live CONFIG_NAME=redbear-mini"
exit 1
fi
if [[ ! -f "$FIRMWARE" ]]; then
echo "ERROR: OVMF firmware not found: $FIRMWARE"
echo "Install: sudo pacman -S edk2-ovmf (or equivalent)"
exit 1
fi
echo "=== P19-1: Multi-Core Driver Stress Test ==="
echo "Image: $IMAGE"
echo "Firmware: $FIRMWARE"
echo "CPUs: $SMP"
echo "Duration: ${DURATION}s"
echo ""
# Create expect-based test script
export DURATION SMP IMAGE FIRMWARE
expect <<'EXPECT_SCRIPT'
log_user 1
set timeout 300
set duration $env(DURATION)
set smp $env(SMP)
set image $env(IMAGE)
set firmware $env(FIRMWARE)
proc expect_or_fail {pattern description} {
expect {
-nocase -re $pattern { return }
timeout {
puts stderr "\nERROR: timed out waiting for $description"
exit 1
}
eof {
puts stderr "\nERROR: QEMU exited while waiting for $description"
exit 1
}
}
}
# Launch QEMU with 4 CPUs, network, serial console
spawn qemu-system-x86_64 \
-name "Red Bear OS SMP Stress Test" \
-machine q35 \
-smp $smp \
-m 2048 \
-drive if=pflash,format=raw,readonly=on,file=$firmware \
-chardev stdio,id=debug,signal=off,mux=on \
-serial chardev:debug \
-mon chardev=debug \
-nographic \
-vga none \
-device virtio-net-pci,netdev=net0 \
-netdev user,id=net0 \
-drive file=$image,format=raw,if=none,id=drv0,snapshot=on \
-device virtio-blk-pci,drive=drv0 \
-enable-kvm \
-cpu host
# Wait for login prompt
puts "\n>>> Waiting for boot..."
expect_or_fail "login:" "login prompt"
# Login as root
send "root\r"
expect_or_fail "assword:" "password prompt"
send "password\r"
expect_or_fail "#|\\$" "shell prompt"
sleep 1
# --- Phase 1: Verify multi-core boot ---
puts "\n>>> Phase 1: Verifying multi-core boot..."
send "cat /scheme/sys/cpu.log 2>/dev/null || echo 'no cpu.log'\r"
expect_or_fail "#|\\$" "cpu.log output"
sleep 1
send "ls /scheme/sys/ 2>/dev/null | head -20\r"
expect_or_fail "#|\\$" "sys scheme listing"
sleep 1
# --- Phase 2: Parallel disk I/O stress ---
puts "\n>>> Phase 2: Starting parallel disk I/O stress (duration: ${duration}s)..."
# Launch 4 parallel dd writers to tmpfs (exercises filesystem scheme + IPC)
send "dd if=/dev/zero of=/tmp/stress-a bs=1M count=50 2>/tmp/stress-a.err &\r"
expect_or_fail "#|\\$" "dd writer A start"
sleep 0.5
send "dd if=/dev/zero of=/tmp/stress-b bs=1M count=50 2>/tmp/stress-b.err &\r"
expect_or_fail "#|\\$" "dd writer B start"
sleep 0.5
send "dd if=/dev/zero of=/tmp/stress-c bs=512 count=100000 2>/tmp/stress-c.err &\r"
expect_or_fail "#|\\$" "dd writer C start"
sleep 0.5
send "dd if=/dev/zero of=/tmp/stress-d bs=4k count=50000 2>/tmp/stress-d.err &\r"
expect_or_fail "#|\\$" "dd writer D start"
sleep 0.5
# --- Phase 3: Parallel reads (exercises IPC) ---
puts "\n>>> Phase 3: Starting parallel filesystem traversal..."
send "ls -laR / > /tmp/ls-out 2>/tmp/ls-err &\r"
expect_or_fail "#|\\$" "ls traversal start"
sleep 0.5
send "cat /etc/passwd > /dev/null 2>&1 &\r"
expect_or_fail "#|\\$" "passwd read start"
sleep 0.5
send "find /tmp -type f > /dev/null 2>&1 &\r"
expect_or_fail "#|\\$" "find traversal start"
sleep 0.5
# --- Phase 4: Wait for stress duration ---
puts "\n>>> Phase 4: Running stress for ${duration}s..."
sleep $duration
# --- Phase 5: Collect results ---
puts "\n>>> Phase 5: Collecting results..."
# Wait for background jobs and check status
send "wait 2>/dev/null; echo STRESS-WAIT-DONE\r"
expect_or_fail "STRESS-WAIT-DONE" "background jobs completion"
sleep 1
# Check if stress files were created
send "ls -la /tmp/stress-* 2>/dev/null; echo STRESS-FILES-DONE\r"
expect_or_fail "STRESS-FILES-DONE" "stress file listing"
sleep 1
# Verify reads work on stress files (read-back test)
send "dd if=/tmp/stress-a of=/dev/null bs=1M count=50 2>/tmp/readback-a.err; echo READBACK-A-STATUS=$?\r"
expect_or_fail "READBACK-A-STATUS=0" "readback A"
sleep 1
# --- Phase 6: Panic detection ---
puts "\n>>> Phase 6: Checking for panics..."
send "dmesg 2>/dev/null | grep -ic PANIC; echo PANIC-CHECK-DONE\r"
expect {
-re "(\[0-9\]+)\r\n.*PANIC-CHECK-DONE" {
set panic_count $expect_out(1,string)
if {$panic_count > 0} {
puts "\n>>> FAIL: $panic_count PANIC(S) detected in dmesg!"
puts "\n>>> Dumping panic context:"
send "dmesg | grep -i PANIC\r"
expect "#|\\$"
exit 1
} else {
puts "\n>>> PASS: No panics detected in dmesg"
}
}
timeout {
puts stderr "\nERROR: timed out during panic check"
exit 1
}
}
sleep 1
# --- Phase 7: Summary ---
puts "\n>>> Phase 7: Final system state..."
send "echo \"UPTIME:\"; uptime 2>/dev/null || cat /scheme/sys/time 2>/dev/null; echo UPTIME-DONE\r"
expect_or_fail "UPTIME-DONE" "uptime output"
sleep 1
send "echo \"PROCS:\"; ps 2>/dev/null || echo 'ps not available'; echo PROCS-DONE\r"
expect_or_fail "PROCS-DONE" "process listing"
sleep 1
# Clean shutdown
puts "\n>>> Shutting down..."
send "shutdown\r"
sleep 5
send "\r"
sleep 2
puts "\n"
puts "============================================"
puts " P19-1 SMP STRESS TEST: PASSED"
puts " CPUs: $smp | Duration: ${duration}s"
puts " No panics, no hangs, I/O completed"
puts "============================================"
expect eof
EXPECT_SCRIPT
exit_code=$?
if [[ $exit_code -eq 0 ]]; then
echo ""
echo "P19-1: PASSED"
else
echo ""
echo "P19-1: FAILED (exit code: $exit_code)"
fi
exit $exit_code