diff --git a/local/scripts/build-amd.sh b/local/scripts/build-amd.sh deleted file mode 100755 index ba25bb27..00000000 --- a/local/scripts/build-amd.sh +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env bash -# Build Red Bear OS with AMD GPU support (Phase P2) -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" - -CONFIG="${1:-my-amd-desktop}" -JOBS="${JOBS:-$(nproc)}" -APPLY_PATCHES="${APPLY_PATCHES:-1}" - -echo "=== Red Bear OS AMD GPU Build ===" -echo "Config: $CONFIG" -echo "Jobs: $JOBS" -echo "Apply patches: $APPLY_PATCHES" -echo "Root: $PROJECT_ROOT" -echo "" - -cd "$PROJECT_ROOT" - -# Step 0: Apply local patches -if [ "$APPLY_PATCHES" = "1" ]; then - echo ">>> Applying local patches..." - - apply_patch_dir() { - local patch_dir="$1" - local target_dir="$2" - local label="$3" - - if [ ! -d "$patch_dir" ]; then - return 0 - fi - - for patch_file in $(ls "$patch_dir"/*.patch 2>/dev/null | sort); do - patch_name=$(basename "$patch_file") - if [ ! -d "$target_dir" ]; then - echo " SKIP $patch_name ($label source not fetched yet)" - continue - fi - if patch --dry-run -p1 -d "$target_dir" < "$patch_file" > /dev/null 2>&1; then - patch -p1 -d "$target_dir" < "$patch_file" > /dev/null 2>&1 - echo " OK $patch_name" - else - echo " SKIP $patch_name (already applied or won't apply)" - fi - done - } - - apply_patch_dir "$PROJECT_ROOT/local/patches/kernel" "$PROJECT_ROOT/recipes/core/kernel/source" "kernel" - apply_patch_dir "$PROJECT_ROOT/local/patches/base" "$PROJECT_ROOT/recipes/core/base/source" "base" - apply_patch_dir "$PROJECT_ROOT/local/patches/relibc" "$PROJECT_ROOT/recipes/core/relibc/source" "relibc" - apply_patch_dir "$PROJECT_ROOT/local/patches/bootloader" "$PROJECT_ROOT/recipes/core/bootloader/source" "bootloader" - apply_patch_dir "$PROJECT_ROOT/local/patches/installer" "$PROJECT_ROOT/recipes/core/installer/source" "installer" - echo "" -fi - -# Step 1: Build cookbook binary if needed -if [ ! -f "target/release/repo" ]; then - echo ">>> Building cookbook binary..." - cargo build --release -fi - -# Step 2: Fetch AMD firmware blobs if missing -FW_DIR="$PROJECT_ROOT/local/firmware/amdgpu" -if [ -z "$(ls -A "$FW_DIR" 2>/dev/null)" ]; then - echo ">>> AMD firmware blobs not found. Run local/scripts/fetch-firmware.sh first." - echo " Skipping firmware fetch. Driver will NOT function without firmware." -else - FW_COUNT=$(ls "$FW_DIR"/*.bin 2>/dev/null | wc -l) - echo ">>> Found $FW_COUNT AMD firmware blobs" -fi - -# Step 3: Build -echo ">>> Building Red Bear OS with config: $CONFIG" -echo ">>> This may take 30-60 minutes on first build..." -CI=1 make all "CONFIG_NAME=$CONFIG" "JOBS=$JOBS" - -echo "" -echo "=== Build Complete ===" -echo "Image: build/x86_64/harddrive.img" -echo "" -echo "To run in QEMU:" -echo " make qemu QEMUFLAGS=\"-m 4G\"" -echo "" -echo "To test on bare metal:" -echo " dd if=build/x86_64/harddrive.img of=/dev/sdX bs=4M status=progress" diff --git a/local/scripts/build-redbear.sh b/local/scripts/build-redbear.sh index a6e43839..c667718c 100755 --- a/local/scripts/build-redbear.sh +++ b/local/scripts/build-redbear.sh @@ -2,14 +2,15 @@ # build-redbear.sh — Build Red Bear OS from upstream base + Red Bear overlay # # Usage: -# ./local/scripts/build-redbear.sh # Default: redbear-desktop -# ./local/scripts/build-redbear.sh redbear-minimal # Minimal validation baseline +# ./local/scripts/build-redbear.sh # Default: redbear-kde +# ./local/scripts/build-redbear.sh redbear-minimal # Minimal validation baseline # ./local/scripts/build-redbear.sh redbear-bluetooth-experimental # First bounded Bluetooth slice -# ./local/scripts/build-redbear.sh redbear-full # Full Red Bear integration target -# ./local/scripts/build-redbear.sh redbear-wayland # Wayland runtime validation profile -# ./local/scripts/build-redbear.sh redbear-kde # KDE Plasma bring-up target -# ./local/scripts/build-redbear.sh redbear-live # Live ISO variant -# APPLY_PATCHES=0 ./local/scripts/build-redbear.sh # Skip patch application +# ./local/scripts/build-redbear.sh redbear-full # Full Red Bear integration target +# ./local/scripts/build-redbear.sh redbear-wayland # Bounded Wayland runtime validation profile +# ./local/scripts/build-redbear.sh redbear-kde # Tracked KWin Wayland desktop target +# ./local/scripts/build-redbear.sh redbear-live # Live ISO variant +# ./local/scripts/build-redbear.sh --upstream redbear-kde # Allow Redox/upstream recipe refresh +# APPLY_PATCHES=0 ./local/scripts/build-redbear.sh # Skip patch application # # This script assumes the Red Bear overlay model: # - upstream-owned sources are refreshable working trees @@ -20,9 +21,56 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" -CONFIG="${1:-redbear-desktop}" +CONFIG="redbear-kde" JOBS="${JOBS:-$(nproc)}" APPLY_PATCHES="${APPLY_PATCHES:-1}" +ALLOW_UPSTREAM=0 + +usage() { + cat <&2 + usage >&2 + exit 1 + ;; + *) + POSITIONAL+=("$1") + ;; + esac + shift +done + +if [ ${#POSITIONAL[@]} -gt 1 ]; then + echo "ERROR: Too many positional arguments" >&2 + usage >&2 + exit 1 +fi + +[ ${#POSITIONAL[@]} -eq 1 ] && CONFIG="${POSITIONAL[0]}" case "$CONFIG" in redbear-desktop|redbear-minimal|redbear-bluetooth-experimental|redbear-full|redbear-wayland|redbear-kde|redbear-live) @@ -40,6 +88,7 @@ echo "========================================" echo "Config: $CONFIG" echo "Jobs: $JOBS" echo "Apply patches: $APPLY_PATCHES" +echo "Upstream: $ALLOW_UPSTREAM" echo "Root: ${PROJECT_ROOT##*/}" echo "========================================" echo "" @@ -112,7 +161,7 @@ if [ "$APPLY_PATCHES" = "1" ]; then apply_patch_dir "$PROJECT_ROOT/local/patches/bootloader" "$PROJECT_ROOT/recipes/core/bootloader/source" "bootloader" apply_patch_dir "$PROJECT_ROOT/local/patches/installer" "$PROJECT_ROOT/recipes/core/installer/source" "installer" - # repo cook refetches nested sources before building; keep relibc clean after patch application + # repo cook can refetch nested sources when --upstream is enabled; keep relibc clean after patch application stash_nested_repo_if_dirty "$PROJECT_ROOT/recipes/core/relibc/source" "relibc" echo "" fi @@ -140,7 +189,13 @@ fi # Step 3: Build echo ">>> Building Red Bear OS with config: $CONFIG" echo ">>> This may take 30-60 minutes on first build..." -CI=1 make all "CONFIG_NAME=$CONFIG" "JOBS=$JOBS" +if [ "$ALLOW_UPSTREAM" -eq 1 ]; then + echo ">>> Upstream recipe refresh enabled" + REPO_OFFLINE=0 COOKBOOK_OFFLINE=false CI=1 make all "CONFIG_NAME=$CONFIG" "JOBS=$JOBS" +else + echo ">>> Upstream recipe refresh disabled (pass --upstream to enable)" + REPO_OFFLINE=1 COOKBOOK_OFFLINE=true CI=1 make all "CONFIG_NAME=$CONFIG" "JOBS=$JOBS" +fi # Step 4: Report ARCH="${ARCH:-$(uname -m)}" @@ -160,8 +215,15 @@ if [ "$CONFIG" = "redbear-minimal" ] || [ "$CONFIG" = "redbear-desktop" ]; then fi if [ "$CONFIG" = "redbear-wayland" ]; then echo "" - echo "To validate the Phase 4 Wayland runtime path:" + echo "To validate the bounded Phase 4 Wayland runtime harness:" echo " ./local/scripts/test-phase4-wayland-qemu.sh" + echo " # in guest: redbear-drm-display-check --vendor amd|intel" +fi +if [ "$CONFIG" = "redbear-kde" ]; then + echo "" + echo "To validate the primary KWin Wayland desktop path:" + echo " ./local/scripts/test-phase6-kde-qemu.sh --check" + echo " # in guest: redbear-drm-display-check --vendor amd|intel" fi echo "" echo "To build live ISO:" diff --git a/local/scripts/extract-linux-quirks.py b/local/scripts/extract-linux-quirks.py index 830ec787..8fe9b364 100644 --- a/local/scripts/extract-linux-quirks.py +++ b/local/scripts/extract-linux-quirks.py @@ -9,11 +9,12 @@ Usage: Outputs TOML quirk entries to stdout that can be appended to files in /etc/quirks.d/ or local/recipes/system/redbear-quirks/source/quirks.d/. -PCI mode: handler-name → flag mapping is heuristic (substring match). Output -requires manual review — the script may misinfer flags. USB table extraction is -direct and does not require review. +PCI mode only maps explicit, high-confidence handler body effects. Unsupported +handlers are omitted instead of guessed. USB table extraction is direct and does +not require review. """ +from dataclasses import dataclass import re import sys @@ -27,6 +28,10 @@ PCI_FLAG_MAP = { "PCI_DEV_FLAGS_BROKEN_PM": "no_pm", } +PCI_HELPER_CALL_MAP = { + "pci_d3cold_disable": "no_d3cold", +} + USB_FLAG_MAP = { "USB_QUIRK_STRING_FETCH_255": "no_string_fetch", "USB_QUIRK_RESET_RESUME": "need_reset", @@ -56,6 +61,15 @@ PCI_FIXUP_RE = re.compile( r'(\w+)\s*\)' ) +PCI_FIXUP_CLASS_RE = re.compile( + r'DECLARE_PCI_FIXUP_CLASS_(?:FINAL|HEADER|EARLY|ENABLE|RESUME|LATE)\s*\(\s*' + r'(?:0x([0-9a-fA-F]+)|PCI_ANY_ID)\s*,\s*' + r'(?:0x([0-9a-fA-F]+)|PCI_ANY_ID)\s*,\s*' + r'(?:0x([0-9a-fA-F]+)|PCI_ANY_ID)\s*,\s*' + r'(?:0x([0-9a-fA-F]+)|PCI_ANY_ID)\s*,\s*' + r'(\w+)\s*\)' +) + DMI_MATCH_RE = re.compile( r'DMI_MATCH\s*\(\s*DMI_([A-Z_]+)\s*,\s*"([^"]+)"\s*\)' ) @@ -66,6 +80,18 @@ USB_QUIRK_TABLE_RE = re.compile( r'([^}]+)\}' ) +PCI_HANDLER_RE = re.compile(r'\b(?P\w+)\s*\([^;{}]*?\)\s*\{', re.DOTALL) +PCI_DEV_FLAGS_ASSIGNMENT_RE = re.compile(r'\b\w+\s*->\s*dev_flags\s*(?:\|=|=)\s*[^;]+;') + + +@dataclass(frozen=True) +class PciFixup: + vendor: int + device: int + handler: str + klass: int | None = None + class_mask: int | None = None + def extract_pci_fixups(source): entries = [] @@ -73,10 +99,86 @@ def extract_pci_fixups(source): vendor = int(m.group(1), 16) if m.group(1) else 0xFFFF device = int(m.group(2), 16) if m.group(2) else 0xFFFF handler = m.group(3) - entries.append((vendor, device, handler)) + entries.append(PciFixup(vendor=vendor, device=device, handler=handler)) + for m in PCI_FIXUP_CLASS_RE.finditer(source): + vendor = int(m.group(1), 16) if m.group(1) else 0xFFFF + device = int(m.group(2), 16) if m.group(2) else 0xFFFF + klass = int(m.group(3), 16) if m.group(3) else 0xFFFF + class_mask = int(m.group(4), 16) if m.group(4) else 0xFFFF + handler = m.group(5) + entries.append( + PciFixup( + vendor=vendor, + device=device, + handler=handler, + klass=klass, + class_mask=class_mask, + ) + ) return entries +def _extract_brace_block(source, brace_start): + depth = 0 + for index in range(brace_start, len(source)): + char = source[index] + if char == "{": + depth += 1 + elif char == "}": + depth -= 1 + if depth == 0: + return source[brace_start + 1 : index] + return None + + +def index_handler_bodies(source, handler_names): + bodies = {} + wanted = set(handler_names) + if not wanted: + return bodies + + for match in PCI_HANDLER_RE.finditer(source): + name = match.group("name") + if name not in wanted or name in bodies: + continue + brace_start = match.end() - 1 + body = _extract_brace_block(source, brace_start) + if body is not None: + bodies[name] = body + return bodies + + +def extract_pci_flags_from_handler(body): + flags = [] + seen = set() + + for match in PCI_DEV_FLAGS_ASSIGNMENT_RE.finditer(body): + statement = match.group(0) + for flag_name, toml_name in PCI_FLAG_MAP.items(): + pattern = re.escape(flag_name) + r'(?:\s|$|\||\)|;|,)' + if re.search(pattern, statement) and toml_name not in seen: + flags.append(toml_name) + seen.add(toml_name) + + for helper_name, toml_name in PCI_HELPER_CALL_MAP.items(): + pattern = rf'\b{re.escape(helper_name)}\s*\(\s*\w+\s*\)' + if re.search(pattern, body) and toml_name not in seen: + flags.append(toml_name) + seen.add(toml_name) + + return flags + + +def map_pci_fixups_to_flags(source): + fixups = extract_pci_fixups(source) + handler_bodies = index_handler_bodies(source, [fixup.handler for fixup in fixups]) + mapped = [] + for fixup in fixups: + flags = extract_pci_flags_from_handler(handler_bodies.get(fixup.handler, "")) + mapped.append((fixup, flags)) + return mapped + + def extract_usb_quirks(source): entries = [] for m in USB_QUIRK_TABLE_RE.finditer(source): @@ -94,14 +196,18 @@ def extract_usb_quirks(source): def format_pci_toml(entries): lines = [] - for vendor, device, flags in entries: + for fixup, flags in entries: if not flags: continue lines.append("[[pci_quirk]]") - if vendor != 0xFFFF: - lines.append(f"vendor = 0x{vendor:04X}") - if device != 0xFFFF: - lines.append(f"device = 0x{device:04X}") + if fixup.vendor != 0xFFFF: + lines.append(f"vendor = 0x{fixup.vendor:04X}") + if fixup.device != 0xFFFF: + lines.append(f"device = 0x{fixup.device:04X}") + if fixup.klass is not None and fixup.klass != 0xFFFF: + lines.append(f"class = 0x{fixup.klass:06X}") + if fixup.class_mask is not None and fixup.class_mask != 0xFFFF: + lines.append(f"class_mask = 0x{fixup.class_mask:06X}") lines.append(f'flags = [{", ".join(f"\"{f}\"" for f in flags)}]') lines.append("") return "\n".join(lines) @@ -226,18 +332,10 @@ def main(): entries = extract_usb_quirks(source) print(format_usb_toml(entries)) else: - entries = extract_pci_fixups(source) - flags_map = PCI_FLAG_MAP - mapped = [] - for vendor, device, handler in entries: - flags = [] - for flag_name, toml_name in flags_map.items(): - if flag_name.lower() in handler.lower(): - flags.append(toml_name) - mapped.append((vendor, device, flags)) - print("# WARNING: PCI handler-name → flag mapping is heuristic.") - print("# WARNING: Output requires manual review before committing.") - print("# USB table extraction is direct and does not need review.") + mapped = map_pci_fixups_to_flags(source) + print("# WARNING: PCI output only includes explicit, high-confidence handler body mappings.") + print("# WARNING: Unsupported Linux PCI quirk handlers are intentionally omitted.") + print("# WARNING: Review generated PCI entries before committing.") print(format_pci_toml(mapped)) diff --git a/local/scripts/fetch-firmware.sh b/local/scripts/fetch-firmware.sh index ad1d6f1a..9bd68cbe 100755 --- a/local/scripts/fetch-firmware.sh +++ b/local/scripts/fetch-firmware.sh @@ -1,28 +1,39 @@ #!/usr/bin/env bash -# Fetch AMD GPU firmware blobs from linux-firmware repository -# These are required for amdgpu driver to function +# Fetch bounded GPU firmware blobs from linux-firmware repository. +# AMD remains the larger set; Intel support here is intentionally limited to +# display-critical DMC blobs for the current bounded startup manifest. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -FIRMWARE_DIR="$SCRIPT_DIR/../firmware/amdgpu" LINUX_FIRMWARE_REPO="https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git" TEMP_DIR=$(mktemp -d) +VENDOR="amd" SUBSET="all" usage() { cat < "$FIRMWARE_DIR/MANIFEST.txt" << 'MANIFEST' +copy_intel_dmc_firmware() { + echo "Copying bounded Intel DMC firmware blobs..." + if [ ! -d "$TEMP_DIR/linux-firmware/i915" ]; then + echo "ERROR: i915 firmware directory not found in linux-firmware" + exit 1 + fi + + local selected_blobs=() + local candidates=( + adlp_dmc.bin + adlp_dmc_ver2_16.bin + tgl_dmc.bin + tgl_dmc_ver2_12.bin + dg2_dmc.bin + dg2_dmc_ver2_06.bin + mtl_dmc.bin + ) + + for blob in "${candidates[@]}"; do + if [ -f "$TEMP_DIR/linux-firmware/i915/$blob" ]; then + selected_blobs+=("$TEMP_DIR/linux-firmware/i915/$blob") + fi + done + + if [ "${#selected_blobs[@]}" -eq 0 ]; then + echo "ERROR: No Intel DMC firmware blobs were found" + exit 1 + fi + + rm -f "$FIRMWARE_DIR"/*.bin + cp -v "${selected_blobs[@]}" "$FIRMWARE_DIR/" + + cat > "$FIRMWARE_DIR/MANIFEST.txt" <<'MANIFEST' +# Intel GPU Firmware for Red Bear OS (bounded startup slice) +# Source: linux-firmware (https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git) +# Scope: display-critical DMC blobs only +# +# This subset is intentionally bounded to startup/display proof for current Intel DRM work. +# It does NOT include GuC/HuC/GSC runtime/render/media firmware. +# +# Current bounded candidates: +# - adlp_dmc.bin / adlp_dmc_ver2_16.bin +# - tgl_dmc.bin / tgl_dmc_ver2_12.bin +# - dg2_dmc.bin / dg2_dmc_ver2_06.bin +# - mtl_dmc.bin +MANIFEST + + echo "Copied ${#selected_blobs[@]} Intel DMC firmware blobs" +} + +case "$VENDOR" in + amd) + copy_amd_firmware + echo "=== Creating firmware manifest ===" + cat > "$FIRMWARE_DIR/MANIFEST.txt" << 'MANIFEST' # AMD GPU Firmware for Red Bear OS # Source: linux-firmware (https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git) # License: Various — see linux-firmware WHENCE file for details @@ -166,8 +263,12 @@ cat > "$FIRMWARE_DIR/MANIFEST.txt" << 'MANIFEST' # Key files for RDNA3 (Navi 31/32/33, gfx11): # psp_13_*_sos.bin, gc_11_0_*.bin, sdma_6_*.bin, dcn_3_1_*.bin MANIFEST - -echo "$FIRMWARE_DIR/MANIFEST.txt created" + echo "$FIRMWARE_DIR/MANIFEST.txt created" + ;; + intel) + copy_intel_dmc_firmware + ;; +esac # Summary echo "" @@ -176,5 +277,5 @@ ls -la "$FIRMWARE_DIR/" | head -20 echo "..." echo "Total: $(ls "$FIRMWARE_DIR/"*.bin 2>/dev/null | wc -l) blobs" echo "" -echo "WARNING: These are proprietary firmware blobs from AMD." +echo "WARNING: These firmware blobs are third-party upstream firmware." echo "They are NOT open source. Verify your license compliance." diff --git a/local/scripts/fetch-sources.sh b/local/scripts/fetch-sources.sh index fa4005db..dd8f51d4 100755 --- a/local/scripts/fetch-sources.sh +++ b/local/scripts/fetch-sources.sh @@ -6,11 +6,12 @@ # sources so they're available in recipes///source/. # # Usage: -# ./local/scripts/fetch-sources.sh # All sources -# ./local/scripts/fetch-sources.sh core # Core packages only -# ./local/scripts/fetch-sources.sh libs tools # Multiple categories +# ./local/scripts/fetch-sources.sh --upstream # All sources +# ./local/scripts/fetch-sources.sh --upstream core # Core packages only +# ./local/scripts/fetch-sources.sh --upstream libs tools # Multiple categories # ./local/scripts/fetch-sources.sh --list # Show available categories # ./local/scripts/fetch-sources.sh --status # Show fetch progress +# ./local/scripts/fetch-sources.sh --help # Show help # # After fetching, sources live at: # recipes///source/ (git repos, tar extractions) @@ -23,6 +24,51 @@ REPO_BIN="$PROJECT_ROOT/target/release/repo" cd "$PROJECT_ROOT" +ALLOW_UPSTREAM=0 +MODE="fetch" +CATEGORIES=() + +usage() { + cat <&2 + usage >&2 + exit 1 + ;; + *) + CATEGORIES+=("$1") + ;; + esac + shift +done + if [ ! -f "$REPO_BIN" ]; then echo ">>> Building cookbook binary..." cargo build --release @@ -52,16 +98,22 @@ show_status() { echo "Sources fetched: $fetched / $total ($pct%)" } -if [ "${1:-}" = "--list" ]; then +if [ "$MODE" = "list" ]; then list_categories exit 0 fi -if [ "${1:-}" = "--status" ]; then +if [ "$MODE" = "status" ]; then show_status exit 0 fi +if [ "$ALLOW_UPSTREAM" -ne 1 ]; then + echo "ERROR: Redox/upstream source fetch is disabled by default." >&2 + echo "Re-run with --upstream to fetch sources." >&2 + exit 1 +fi + echo "========================================" echo " Redox Source Fetcher" echo "========================================" @@ -110,14 +162,14 @@ fetch_category() { [ "$ok" -eq 0 ] && [ "$skip" -eq 0 ] && [ "$fail" -gt 0 ] && echo " ❌ $cat: $fail failed" } -if [ $# -eq 0 ]; then +if [ ${#CATEGORIES[@]} -eq 0 ]; then echo ">>> Fetching ALL recipe sources..." echo "" for cat in $ALL_CATEGORIES; do fetch_category "$cat" done else - for cat in "$@"; do + for cat in "${CATEGORIES[@]}"; do if [ -d "recipes/$cat" ]; then echo ">>> Fetching category: $cat" fetch_category "$cat" diff --git a/local/scripts/sync-upstream.sh b/local/scripts/sync-upstream.sh index 5520e447..fd5fa318 100755 --- a/local/scripts/sync-upstream.sh +++ b/local/scripts/sync-upstream.sh @@ -24,18 +24,23 @@ UPSTREAM_BRANCH="${UPSTREAM_BRANCH:-master}" DRY_RUN=0 NO_MERGE=0 +usage() { + echo "Usage: $0 [--dry-run] [--no-merge]" + echo " --dry-run Show what would happen without making changes" + echo " --no-merge Only fetch and check patch conflicts" +} + for arg in "$@"; do case "$arg" in --dry-run) DRY_RUN=1 ;; --no-merge) NO_MERGE=1 ;; --help|-h) - echo "Usage: $0 [--dry-run] [--no-merge]" - echo " --dry-run Show what would happen without making changes" - echo " --no-merge Only fetch and check patch conflicts" + usage exit 0 ;; *) echo "Unknown argument: $arg" + usage >&2 exit 1 ;; esac diff --git a/local/scripts/test-amd-gpu.sh b/local/scripts/test-amd-gpu.sh index ba709142..e1e454af 100755 --- a/local/scripts/test-amd-gpu.sh +++ b/local/scripts/test-amd-gpu.sh @@ -1,43 +1,5 @@ #!/usr/bin/env bash -# Test AMD GPU driver on Red Bear OS -# Run this inside Red Bear OS (or via QEMU serial console) set -euo pipefail -echo "=== AMD GPU Driver Test ===" -echo "" - -# Check if scheme:drm exists -if [ -e "/scheme/drm" ]; then - echo "✅ scheme:drm registered" -else - echo "❌ scheme:drm NOT found — redox-drm daemon not running?" - exit 1 -fi - -# Check card0 -if [ -e "/scheme/drm/card0" ]; then - echo "✅ /scheme/drm/card0 exists" -else - echo "❌ /scheme/drm/card0 NOT found — AMD GPU not detected?" - exit 1 -fi - -# Try to read connector info -echo "" -echo "=== Connector Info ===" -if command -v modetest &>/dev/null; then - modetest -M amd 2>&1 | head -50 -else - echo "modetest not available — reading raw scheme" - # Read from scheme directly - cat /scheme/drm/card0 2>&1 | head -20 || true -fi - -echo "" -echo "=== PCI Devices (GPU) ===" -ls /scheme/pci/ 2>/dev/null | while read -r entry; do - echo " $entry" -done - -echo "" -echo "=== Test Complete ===" +script_dir="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)" +exec "${script_dir}/test-drm-display-runtime.sh" --vendor amd "$@" diff --git a/local/scripts/test-drm-display-runtime.sh b/local/scripts/test-drm-display-runtime.sh new file mode 100755 index 00000000..6935c902 --- /dev/null +++ b/local/scripts/test-drm-display-runtime.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +set -euo pipefail + +vendor="" +card_path="/scheme/drm/card0" +modeset_spec="" + +usage() { + cat <<'USAGE' +Usage: test-drm-display-runtime.sh --vendor amd|intel [--card /scheme/drm/card0] [--modeset CONNECTOR:MODE] + +Bounded DRM/KMS display validation harness. + +This proves only display-path evidence: + - scheme:drm registration + - DRM card reachability + - connector/mode enumeration + - optional bounded modeset proof when a specific CONNECTOR:MODE is supplied + +This does NOT prove render command submission, fence semantics, or hardware rendering. +USAGE +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --vendor) + vendor="${2:-}" + shift 2 + ;; + --card) + card_path="${2:-}" + shift 2 + ;; + --modeset) + modeset_spec="${2:-}" + shift 2 + ;; + --help|-h) + usage + exit 0 + ;; + *) + echo "ERROR: unknown argument: $1" >&2 + usage >&2 + exit 1 + ;; + esac +done + +if [[ "$vendor" != "amd" && "$vendor" != "intel" ]]; then + echo "ERROR: --vendor must be amd or intel" >&2 + exit 1 +fi + +echo "=== Red Bear DRM Display Runtime Check ===" +echo "DRM_VENDOR=${vendor}" +echo "DRM_CARD=${card_path}" + +command=(redbear-drm-display-check --vendor "$vendor" --card "$card_path") +if [[ -n "$modeset_spec" ]]; then + command+=(--modeset "$modeset_spec") +fi + +exec "${command[@]}" diff --git a/local/scripts/test-intel-gpu.sh b/local/scripts/test-intel-gpu.sh new file mode 100755 index 00000000..d5b0bec8 --- /dev/null +++ b/local/scripts/test-intel-gpu.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -euo pipefail + +script_dir="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)" +exec "${script_dir}/test-drm-display-runtime.sh" --vendor intel "$@" diff --git a/local/scripts/test-phase1-desktop-substrate.sh b/local/scripts/test-phase1-desktop-substrate.sh index 03884965..2b086ed9 100755 --- a/local/scripts/test-phase1-desktop-substrate.sh +++ b/local/scripts/test-phase1-desktop-substrate.sh @@ -111,6 +111,9 @@ run_guest_checks() { echo " FAIL /scheme/drm does not exist" failures=$((failures + 1)) fi + if command -v redbear-drm-display-check >/dev/null 2>&1; then + echo " NOTE redbear-drm-display-check available (run manually for bounded display validation)" + fi echo echo "--- health check summary ---" diff --git a/local/scripts/test-phase4-wayland-qemu.sh b/local/scripts/test-phase4-wayland-qemu.sh index c37a53f6..53373441 100644 --- a/local/scripts/test-phase4-wayland-qemu.sh +++ b/local/scripts/test-phase4-wayland-qemu.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash -# Launch the Phase 4 Wayland path in QEMU using the repo's Wayland profile. +# Launch the Phase 4 Wayland validation path in QEMU using the repo's Wayland profile. # This script validates the current bounded software-path Wayland runtime slice. +# It is a bounded validation harness, not the production desktop path. # It does NOT currently prove a hardware-accelerated desktop path in QEMU. set -euo pipefail @@ -36,8 +37,8 @@ Examples: ./local/scripts/test-phase4-wayland-qemu.sh --check ./local/scripts/test-phase4-wayland-qemu.sh -m 4G -Expected runtime path: - orbital -> orbital-wayland -> smallvil -> wayland-session +Expected validation path: + display session -> validation launcher -> compositor -> wayland-session Important: the current harness uses '-vga std' and today still surfaces llvmpipe in-guest. @@ -99,8 +100,9 @@ echo "Suggested in-guest checks:" echo " redbear-info --json" echo " netctl status" echo " redbear-phase4-wayland-check" -echo " smallvil should be the primary compositor path" +echo " the validation compositor should own the bounded runtime path" echo " qt6-wayland-smoke should leave a success marker via wayland-session" +echo " production desktop direction is redbear-kde -> kwin_wayland" echo if [[ "$check_mode" -eq 1 ]]; then @@ -115,9 +117,8 @@ send "password\r" expect "Type 'help' for available commands." send "redbear-phase4-wayland-check\r" expect "Red Bear OS Phase 4 Wayland Runtime Check" -expect "orbital-wayland" +expect "redbear-validation-session" expect "wayland-session" -expect "smallvil" expect "/home/root/.qt6-bootstrap-minimal.ok" expect "/home/root/.qt6-plugin-minimal.ok" expect "/home/root/.qt6-wayland-smoke-minimal.ok" diff --git a/local/scripts/test-phase4-wayland-runtime.sh b/local/scripts/test-phase4-wayland-runtime.sh index 5fad3551..89a5e21b 100644 --- a/local/scripts/test-phase4-wayland-runtime.sh +++ b/local/scripts/test-phase4-wayland-runtime.sh @@ -18,9 +18,8 @@ require_command() { fi } -require_command orbital-wayland "orbital-wayland launcher is installed" +require_command redbear-validation-session "validation launcher is installed" require_command wayland-session "wayland-session launcher is installed" -require_command smallvil "smallvil compositor is installed" require_command qt6-wayland-smoke "qt6-wayland-smoke is installed" require_command qt6-bootstrap-check "qt6-bootstrap-check is installed" require_command qt6-plugin-check "qt6-plugin-check is installed" @@ -54,7 +53,7 @@ fi echo echo "=== Phase 4 launch surface ===" -echo "orbital-wayland, smallvil, and the Qt6 Phase 4 smoke helpers are present on the wayland profile." +echo "The validation launcher and the Qt6 Phase 4 smoke helpers are present on the wayland profile." echo "Run this script inside the guest, or use redbear-phase4-wayland-check as the canonical validator." echo echo "=== Test Complete ===" diff --git a/local/scripts/test-phase6-kde-qemu.sh b/local/scripts/test-phase6-kde-qemu.sh index 5450565f..3fd8f002 100644 --- a/local/scripts/test-phase6-kde-qemu.sh +++ b/local/scripts/test-phase6-kde-qemu.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Launch or validate the Phase 6 KDE runtime surface in QEMU. +# Launch or validate the Phase 6 primary KWin Wayland runtime surface in QEMU. set -euo pipefail @@ -27,7 +27,7 @@ usage() { cat <<'USAGE' Usage: test-phase6-kde-qemu.sh [--check] [extra qemu args...] -Boot or validate the Red Bear OS Phase 6 KDE session surface on redbear-kde. +Boot or validate the Red Bear OS primary KWin Wayland session surface on redbear-kde. USAGE } @@ -112,7 +112,7 @@ send "password\r" expect "Type 'help' for available commands." send "redbear-phase6-kde-check\r" expect "Red Bear OS Phase 6 KDE Runtime Check" -expect "orbital-kde" + expect "redbear-kde-session" expect "kwin_wayland" expect { "PHASE6_UPOWER_ENUMERATE=ok" {} diff --git a/local/scripts/test_extract_linux_quirks.py b/local/scripts/test_extract_linux_quirks.py new file mode 100644 index 00000000..a7fe3a49 --- /dev/null +++ b/local/scripts/test_extract_linux_quirks.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +import importlib.util +import pathlib +import unittest + + +SCRIPT_PATH = pathlib.Path(__file__).with_name("extract-linux-quirks.py") +SPEC = importlib.util.spec_from_file_location("extract_linux_quirks", SCRIPT_PATH) +if SPEC is None or SPEC.loader is None: + raise RuntimeError(f"failed to load module spec for {SCRIPT_PATH}") +MODULE = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(MODULE) + + +class ExtractLinuxQuirksPciTests(unittest.TestCase): + def test_direct_flag_assignment_maps_to_redbear_flag(self): + source = """ +static void quirk_no_d3(struct pci_dev *dev) +{ + dev->dev_flags |= PCI_DEV_FLAGS_NO_D3; +} + +DECLARE_PCI_FIXUP_FINAL(0x1002, 0x67DF, quirk_no_d3); +""" + + mapped = MODULE.map_pci_fixups_to_flags(source) + + self.assertEqual(mapped[0][1], ["no_d3cold"]) + + def test_multi_flag_assignment_maps_all_supported_flags(self): + source = """ +static void quirk_multi(struct pci_dev *dev) +{ + dev->dev_flags |= PCI_DEV_FLAGS_NO_MSI | PCI_DEV_FLAGS_NO_MSIX | PCI_DEV_FLAGS_NO_ASPM; +} + +DECLARE_PCI_FIXUP_HEADER(0x8086, 0x1234, quirk_multi); +""" + + mapped = MODULE.map_pci_fixups_to_flags(source) + + self.assertEqual(mapped[0][1], ["no_aspm", "no_msi", "no_msix"]) + + def test_helper_call_maps_no_d3cold(self): + source = """ +static void quirk_disable_d3cold(struct pci_dev *pdev) +{ + pci_d3cold_disable(pdev); +} + +DECLARE_PCI_FIXUP_ENABLE(0x1022, 0x1481, quirk_disable_d3cold); +""" + + mapped = MODULE.map_pci_fixups_to_flags(source) + + self.assertEqual(mapped[0][1], ["no_d3cold"]) + + def test_unsupported_handler_body_yields_no_flags(self): + source = """ +static void quirk_vendor_workaround(struct pci_dev *dev) +{ + pci_info(dev, "workaround only\n"); +} + +DECLARE_PCI_FIXUP_LATE(0x10DE, 0x1C82, quirk_vendor_workaround); +""" + + mapped = MODULE.map_pci_fixups_to_flags(source) + + self.assertEqual(mapped[0][1], []) + self.assertEqual(MODULE.format_pci_toml(mapped), "") + + def test_handler_name_false_positive_regression_is_ignored(self): + source = """ +static void quirk_pci_dev_flags_no_msi_name_only(struct pci_dev *dev) +{ + pci_info(dev, "name should not drive extraction\n"); +} + +DECLARE_PCI_FIXUP_FINAL(0x1234, 0x5678, quirk_pci_dev_flags_no_msi_name_only); +""" + + mapped = MODULE.map_pci_fixups_to_flags(source) + + self.assertEqual(mapped[0][1], []) + + def test_class_fixup_carries_class_fields_into_toml_output(self): + source = """ +static void quirk_class_based(struct pci_dev *dev) +{ + dev->dev_flags |= PCI_DEV_FLAGS_ASSIGN_BARS | PCI_DEV_FLAGS_BROKEN_PM; +} + +DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_ANY_ID, PCI_ANY_ID, 0x030000, 0xFFFF00, quirk_class_based); +""" + + mapped = MODULE.map_pci_fixups_to_flags(source) + toml_output = MODULE.format_pci_toml(mapped) + + self.assertEqual(mapped[0][1], ["disable_bar_sizing", "no_pm"]) + self.assertIn("[[pci_quirk]]", toml_output) + self.assertIn("class = 0x030000", toml_output) + self.assertIn("class_mask = 0xFFFF00", toml_output) + self.assertIn('flags = ["disable_bar_sizing", "no_pm"]', toml_output) + + +if __name__ == "__main__": + unittest.main()