Refresh local build and test scripts, add DRM and Intel GPU test scripts

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-18 17:59:15 +01:00
parent b029ab628f
commit 841773c3a7
14 changed files with 572 additions and 198 deletions
-86
View File
@@ -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"
+73 -11
View File
@@ -2,14 +2,15 @@
# build-redbear.sh — Build Red Bear OS from upstream base + Red Bear overlay # build-redbear.sh — Build Red Bear OS from upstream base + Red Bear overlay
# #
# Usage: # Usage:
# ./local/scripts/build-redbear.sh # Default: redbear-desktop # ./local/scripts/build-redbear.sh # Default: redbear-kde
# ./local/scripts/build-redbear.sh redbear-minimal # Minimal validation baseline # ./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-bluetooth-experimental # First bounded Bluetooth slice
# ./local/scripts/build-redbear.sh redbear-full # Full Red Bear integration target # ./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-wayland # Bounded Wayland runtime validation profile
# ./local/scripts/build-redbear.sh redbear-kde # KDE Plasma bring-up target # ./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 redbear-live # Live ISO variant
# APPLY_PATCHES=0 ./local/scripts/build-redbear.sh # Skip patch application # ./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: # This script assumes the Red Bear overlay model:
# - upstream-owned sources are refreshable working trees # - upstream-owned sources are refreshable working trees
@@ -20,9 +21,56 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
CONFIG="${1:-redbear-desktop}" CONFIG="redbear-kde"
JOBS="${JOBS:-$(nproc)}" JOBS="${JOBS:-$(nproc)}"
APPLY_PATCHES="${APPLY_PATCHES:-1}" APPLY_PATCHES="${APPLY_PATCHES:-1}"
ALLOW_UPSTREAM=0
usage() {
cat <<EOF
Usage: $(basename "$0") [OPTIONS] [CONFIG]
Build a tracked Red Bear OS profile.
Options:
--upstream Allow Redox/upstream recipe source refresh during build
-h, --help Show this help
Configs:
redbear-desktop, redbear-minimal, redbear-bluetooth-experimental,
redbear-full, redbear-wayland, redbear-kde, redbear-live
EOF
}
POSITIONAL=()
while [ $# -gt 0 ]; do
case "$1" in
--upstream)
ALLOW_UPSTREAM=1
;;
-h|--help)
usage
exit 0
;;
-*)
echo "Unknown option: $1" >&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 case "$CONFIG" in
redbear-desktop|redbear-minimal|redbear-bluetooth-experimental|redbear-full|redbear-wayland|redbear-kde|redbear-live) redbear-desktop|redbear-minimal|redbear-bluetooth-experimental|redbear-full|redbear-wayland|redbear-kde|redbear-live)
@@ -40,6 +88,7 @@ echo "========================================"
echo "Config: $CONFIG" echo "Config: $CONFIG"
echo "Jobs: $JOBS" echo "Jobs: $JOBS"
echo "Apply patches: $APPLY_PATCHES" echo "Apply patches: $APPLY_PATCHES"
echo "Upstream: $ALLOW_UPSTREAM"
echo "Root: ${PROJECT_ROOT##*/}" echo "Root: ${PROJECT_ROOT##*/}"
echo "========================================" echo "========================================"
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/bootloader" "$PROJECT_ROOT/recipes/core/bootloader/source" "bootloader"
apply_patch_dir "$PROJECT_ROOT/local/patches/installer" "$PROJECT_ROOT/recipes/core/installer/source" "installer" 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" stash_nested_repo_if_dirty "$PROJECT_ROOT/recipes/core/relibc/source" "relibc"
echo "" echo ""
fi fi
@@ -140,7 +189,13 @@ fi
# Step 3: Build # Step 3: Build
echo ">>> Building Red Bear OS with config: $CONFIG" echo ">>> Building Red Bear OS with config: $CONFIG"
echo ">>> This may take 30-60 minutes on first build..." 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 # Step 4: Report
ARCH="${ARCH:-$(uname -m)}" ARCH="${ARCH:-$(uname -m)}"
@@ -160,8 +215,15 @@ if [ "$CONFIG" = "redbear-minimal" ] || [ "$CONFIG" = "redbear-desktop" ]; then
fi fi
if [ "$CONFIG" = "redbear-wayland" ]; then if [ "$CONFIG" = "redbear-wayland" ]; then
echo "" 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 " ./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 fi
echo "" echo ""
echo "To build live ISO:" echo "To build live ISO:"
+119 -21
View File
@@ -9,11 +9,12 @@ Usage:
Outputs TOML quirk entries to stdout that can be appended to files in 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/. /etc/quirks.d/ or local/recipes/system/redbear-quirks/source/quirks.d/.
PCI mode: handler-name → flag mapping is heuristic (substring match). Output PCI mode only maps explicit, high-confidence handler body effects. Unsupported
requires manual review — the script may misinfer flags. USB table extraction is handlers are omitted instead of guessed. USB table extraction is direct and does
direct and does not require review. not require review.
""" """
from dataclasses import dataclass
import re import re
import sys import sys
@@ -27,6 +28,10 @@ PCI_FLAG_MAP = {
"PCI_DEV_FLAGS_BROKEN_PM": "no_pm", "PCI_DEV_FLAGS_BROKEN_PM": "no_pm",
} }
PCI_HELPER_CALL_MAP = {
"pci_d3cold_disable": "no_d3cold",
}
USB_FLAG_MAP = { USB_FLAG_MAP = {
"USB_QUIRK_STRING_FETCH_255": "no_string_fetch", "USB_QUIRK_STRING_FETCH_255": "no_string_fetch",
"USB_QUIRK_RESET_RESUME": "need_reset", "USB_QUIRK_RESET_RESUME": "need_reset",
@@ -56,6 +61,15 @@ PCI_FIXUP_RE = re.compile(
r'(\w+)\s*\)' 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( DMI_MATCH_RE = re.compile(
r'DMI_MATCH\s*\(\s*DMI_([A-Z_]+)\s*,\s*"([^"]+)"\s*\)' r'DMI_MATCH\s*\(\s*DMI_([A-Z_]+)\s*,\s*"([^"]+)"\s*\)'
) )
@@ -66,6 +80,18 @@ USB_QUIRK_TABLE_RE = re.compile(
r'([^}]+)\}' r'([^}]+)\}'
) )
PCI_HANDLER_RE = re.compile(r'\b(?P<name>\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): def extract_pci_fixups(source):
entries = [] entries = []
@@ -73,10 +99,86 @@ def extract_pci_fixups(source):
vendor = int(m.group(1), 16) if m.group(1) else 0xFFFF vendor = int(m.group(1), 16) if m.group(1) else 0xFFFF
device = int(m.group(2), 16) if m.group(2) else 0xFFFF device = int(m.group(2), 16) if m.group(2) else 0xFFFF
handler = m.group(3) 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 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): def extract_usb_quirks(source):
entries = [] entries = []
for m in USB_QUIRK_TABLE_RE.finditer(source): for m in USB_QUIRK_TABLE_RE.finditer(source):
@@ -94,14 +196,18 @@ def extract_usb_quirks(source):
def format_pci_toml(entries): def format_pci_toml(entries):
lines = [] lines = []
for vendor, device, flags in entries: for fixup, flags in entries:
if not flags: if not flags:
continue continue
lines.append("[[pci_quirk]]") lines.append("[[pci_quirk]]")
if vendor != 0xFFFF: if fixup.vendor != 0xFFFF:
lines.append(f"vendor = 0x{vendor:04X}") lines.append(f"vendor = 0x{fixup.vendor:04X}")
if device != 0xFFFF: if fixup.device != 0xFFFF:
lines.append(f"device = 0x{device:04X}") 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(f'flags = [{", ".join(f"\"{f}\"" for f in flags)}]')
lines.append("") lines.append("")
return "\n".join(lines) return "\n".join(lines)
@@ -226,18 +332,10 @@ def main():
entries = extract_usb_quirks(source) entries = extract_usb_quirks(source)
print(format_usb_toml(entries)) print(format_usb_toml(entries))
else: else:
entries = extract_pci_fixups(source) mapped = map_pci_fixups_to_flags(source)
flags_map = PCI_FLAG_MAP print("# WARNING: PCI output only includes explicit, high-confidence handler body mappings.")
mapped = [] print("# WARNING: Unsupported Linux PCI quirk handlers are intentionally omitted.")
for vendor, device, handler in entries: print("# WARNING: Review generated PCI entries before committing.")
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.")
print(format_pci_toml(mapped)) print(format_pci_toml(mapped))
+119 -18
View File
@@ -1,28 +1,39 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Fetch AMD GPU firmware blobs from linux-firmware repository # Fetch bounded GPU firmware blobs from linux-firmware repository.
# These are required for amdgpu driver to function # 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 set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" 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" LINUX_FIRMWARE_REPO="https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git"
TEMP_DIR=$(mktemp -d) TEMP_DIR=$(mktemp -d)
VENDOR="amd"
SUBSET="all" SUBSET="all"
usage() { usage() {
cat <<EOF cat <<EOF
Usage: $(basename "$0") [--subset all|rdna] Usage: $(basename "$0") [--vendor amd|intel] [--subset all|rdna|dmc]
Fetch AMD GPU firmware blobs from linux-firmware. Fetch bounded GPU firmware blobs from linux-firmware.
Options: Options:
--subset all Fetch the full amdgpu firmware set (default) --vendor amd Fetch AMD GPU firmware (default)
--subset rdna Fetch only RDNA2/RDNA3-oriented firmware blobs --vendor intel Fetch bounded Intel display-critical DMC firmware set
--subset all Fetch the full AMD amdgpu firmware set (default for AMD)
--subset rdna Fetch only RDNA2/RDNA3-oriented AMD firmware blobs
--subset dmc Fetch bounded Intel DMC display firmware set (default for Intel)
-h, --help Show this help text -h, --help Show this help text
EOF EOF
} }
set_firmware_dir() {
case "$VENDOR" in
amd) FIRMWARE_DIR="$SCRIPT_DIR/../firmware/amdgpu" ;;
intel) FIRMWARE_DIR="$SCRIPT_DIR/../firmware/i915" ;;
esac
}
cleanup() { cleanup() {
rm -rf "$TEMP_DIR" rm -rf "$TEMP_DIR"
} }
@@ -40,6 +51,15 @@ while [ "$#" -gt 0 ]; do
SUBSET="$2" SUBSET="$2"
shift 2 shift 2
;; ;;
--vendor)
if [ "$#" -lt 2 ]; then
echo "ERROR: --vendor requires a value"
usage
exit 1
fi
VENDOR="$2"
shift 2
;;
-h|--help) -h|--help)
usage usage
exit 0 exit 0
@@ -52,17 +72,41 @@ while [ "$#" -gt 0 ]; do
esac esac
done done
case "$SUBSET" in case "$VENDOR" in
all|rdna) amd)
case "$SUBSET" in
all|rdna) ;;
*)
echo "ERROR: Unsupported AMD subset: $SUBSET"
usage
exit 1
;;
esac
;;
intel)
if [ "$SUBSET" = "all" ]; then
SUBSET="dmc"
fi
case "$SUBSET" in
dmc) ;;
*)
echo "ERROR: Unsupported Intel subset: $SUBSET"
usage
exit 1
;;
esac
;; ;;
*) *)
echo "ERROR: Unsupported subset: $SUBSET" echo "ERROR: Unsupported vendor: $VENDOR"
usage usage
exit 1 exit 1
;; ;;
esac esac
echo "=== AMD GPU Firmware Fetcher ===" set_firmware_dir
echo "=== GPU Firmware Fetcher ==="
echo "Vendor: $VENDOR"
echo "Target: $FIRMWARE_DIR" echo "Target: $FIRMWARE_DIR"
echo "Subset: $SUBSET" echo "Subset: $SUBSET"
@@ -73,7 +117,7 @@ git clone --depth 1 "$LINUX_FIRMWARE_REPO" "$TEMP_DIR/linux-firmware"
# Create target directory # Create target directory
mkdir -p "$FIRMWARE_DIR" mkdir -p "$FIRMWARE_DIR"
# Copy AMD GPU firmware copy_amd_firmware() {
echo "Copying AMD GPU firmware blobs..." echo "Copying AMD GPU firmware blobs..."
if [ -d "$TEMP_DIR/linux-firmware/amdgpu" ]; then if [ -d "$TEMP_DIR/linux-firmware/amdgpu" ]; then
shopt -s nullglob shopt -s nullglob
@@ -145,10 +189,63 @@ else
echo "ERROR: amdgpu firmware directory not found in linux-firmware" echo "ERROR: amdgpu firmware directory not found in linux-firmware"
exit 1 exit 1
fi fi
}
# Also create a listing of which firmware blobs map to which ASICs copy_intel_dmc_firmware() {
echo "=== Creating firmware manifest ===" echo "Copying bounded Intel DMC firmware blobs..."
cat > "$FIRMWARE_DIR/MANIFEST.txt" << 'MANIFEST' 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 # AMD GPU Firmware for Red Bear OS
# Source: linux-firmware (https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git) # 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 # 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): # Key files for RDNA3 (Navi 31/32/33, gfx11):
# psp_13_*_sos.bin, gc_11_0_*.bin, sdma_6_*.bin, dcn_3_1_*.bin # psp_13_*_sos.bin, gc_11_0_*.bin, sdma_6_*.bin, dcn_3_1_*.bin
MANIFEST MANIFEST
echo "$FIRMWARE_DIR/MANIFEST.txt created"
echo "$FIRMWARE_DIR/MANIFEST.txt created" ;;
intel)
copy_intel_dmc_firmware
;;
esac
# Summary # Summary
echo "" echo ""
@@ -176,5 +277,5 @@ ls -la "$FIRMWARE_DIR/" | head -20
echo "..." echo "..."
echo "Total: $(ls "$FIRMWARE_DIR/"*.bin 2>/dev/null | wc -l) blobs" echo "Total: $(ls "$FIRMWARE_DIR/"*.bin 2>/dev/null | wc -l) blobs"
echo "" 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." echo "They are NOT open source. Verify your license compliance."
+59 -7
View File
@@ -6,11 +6,12 @@
# sources so they're available in recipes/<category>/<pkg>/source/. # sources so they're available in recipes/<category>/<pkg>/source/.
# #
# Usage: # Usage:
# ./local/scripts/fetch-sources.sh # All sources # ./local/scripts/fetch-sources.sh --upstream # All sources
# ./local/scripts/fetch-sources.sh core # Core packages only # ./local/scripts/fetch-sources.sh --upstream core # Core packages only
# ./local/scripts/fetch-sources.sh libs tools # Multiple categories # ./local/scripts/fetch-sources.sh --upstream libs tools # Multiple categories
# ./local/scripts/fetch-sources.sh --list # Show available categories # ./local/scripts/fetch-sources.sh --list # Show available categories
# ./local/scripts/fetch-sources.sh --status # Show fetch progress # ./local/scripts/fetch-sources.sh --status # Show fetch progress
# ./local/scripts/fetch-sources.sh --help # Show help
# #
# After fetching, sources live at: # After fetching, sources live at:
# recipes/<category>/<pkg>/source/ (git repos, tar extractions) # recipes/<category>/<pkg>/source/ (git repos, tar extractions)
@@ -23,6 +24,51 @@ REPO_BIN="$PROJECT_ROOT/target/release/repo"
cd "$PROJECT_ROOT" cd "$PROJECT_ROOT"
ALLOW_UPSTREAM=0
MODE="fetch"
CATEGORIES=()
usage() {
cat <<EOF
Usage: $(basename "$0") [OPTIONS] [CATEGORY ...]
Fetch recipe sources for browsing and editing.
Options:
--upstream Allow Redox/upstream source fetch
--list Show available categories
--status Show fetch progress
-h, --help Show this help
EOF
}
while [ $# -gt 0 ]; do
case "$1" in
--upstream)
ALLOW_UPSTREAM=1
;;
--list)
MODE="list"
;;
--status)
MODE="status"
;;
-h|--help)
usage
exit 0
;;
-*)
echo "Unknown option: $1" >&2
usage >&2
exit 1
;;
*)
CATEGORIES+=("$1")
;;
esac
shift
done
if [ ! -f "$REPO_BIN" ]; then if [ ! -f "$REPO_BIN" ]; then
echo ">>> Building cookbook binary..." echo ">>> Building cookbook binary..."
cargo build --release cargo build --release
@@ -52,16 +98,22 @@ show_status() {
echo "Sources fetched: $fetched / $total ($pct%)" echo "Sources fetched: $fetched / $total ($pct%)"
} }
if [ "${1:-}" = "--list" ]; then if [ "$MODE" = "list" ]; then
list_categories list_categories
exit 0 exit 0
fi fi
if [ "${1:-}" = "--status" ]; then if [ "$MODE" = "status" ]; then
show_status show_status
exit 0 exit 0
fi 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 "========================================"
echo " Redox Source Fetcher" echo " Redox Source Fetcher"
echo "========================================" echo "========================================"
@@ -110,14 +162,14 @@ fetch_category() {
[ "$ok" -eq 0 ] && [ "$skip" -eq 0 ] && [ "$fail" -gt 0 ] && echo "$cat: $fail failed" [ "$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 ">>> Fetching ALL recipe sources..."
echo "" echo ""
for cat in $ALL_CATEGORIES; do for cat in $ALL_CATEGORIES; do
fetch_category "$cat" fetch_category "$cat"
done done
else else
for cat in "$@"; do for cat in "${CATEGORIES[@]}"; do
if [ -d "recipes/$cat" ]; then if [ -d "recipes/$cat" ]; then
echo ">>> Fetching category: $cat" echo ">>> Fetching category: $cat"
fetch_category "$cat" fetch_category "$cat"
+8 -3
View File
@@ -24,18 +24,23 @@ UPSTREAM_BRANCH="${UPSTREAM_BRANCH:-master}"
DRY_RUN=0 DRY_RUN=0
NO_MERGE=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 for arg in "$@"; do
case "$arg" in case "$arg" in
--dry-run) DRY_RUN=1 ;; --dry-run) DRY_RUN=1 ;;
--no-merge) NO_MERGE=1 ;; --no-merge) NO_MERGE=1 ;;
--help|-h) --help|-h)
echo "Usage: $0 [--dry-run] [--no-merge]" usage
echo " --dry-run Show what would happen without making changes"
echo " --no-merge Only fetch and check patch conflicts"
exit 0 exit 0
;; ;;
*) *)
echo "Unknown argument: $arg" echo "Unknown argument: $arg"
usage >&2
exit 1 exit 1
;; ;;
esac esac
+2 -40
View File
@@ -1,43 +1,5 @@
#!/usr/bin/env bash #!/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 set -euo pipefail
echo "=== AMD GPU Driver Test ===" script_dir="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
echo "" exec "${script_dir}/test-drm-display-runtime.sh" --vendor amd "$@"
# 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 ==="
+64
View File
@@ -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[@]}"
+5
View File
@@ -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 "$@"
@@ -111,6 +111,9 @@ run_guest_checks() {
echo " FAIL /scheme/drm does not exist" echo " FAIL /scheme/drm does not exist"
failures=$((failures + 1)) failures=$((failures + 1))
fi 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
echo "--- health check summary ---" echo "--- health check summary ---"
+7 -6
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env bash #!/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. # 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. # It does NOT currently prove a hardware-accelerated desktop path in QEMU.
set -euo pipefail set -euo pipefail
@@ -36,8 +37,8 @@ Examples:
./local/scripts/test-phase4-wayland-qemu.sh --check ./local/scripts/test-phase4-wayland-qemu.sh --check
./local/scripts/test-phase4-wayland-qemu.sh -m 4G ./local/scripts/test-phase4-wayland-qemu.sh -m 4G
Expected runtime path: Expected validation path:
orbital -> orbital-wayland -> smallvil -> wayland-session display session -> validation launcher -> compositor -> wayland-session
Important: Important:
the current harness uses '-vga std' and today still surfaces llvmpipe in-guest. 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 " redbear-info --json"
echo " netctl status" echo " netctl status"
echo " redbear-phase4-wayland-check" 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 " qt6-wayland-smoke should leave a success marker via wayland-session"
echo " production desktop direction is redbear-kde -> kwin_wayland"
echo echo
if [[ "$check_mode" -eq 1 ]]; then if [[ "$check_mode" -eq 1 ]]; then
@@ -115,9 +117,8 @@ send "password\r"
expect "Type 'help' for available commands." expect "Type 'help' for available commands."
send "redbear-phase4-wayland-check\r" send "redbear-phase4-wayland-check\r"
expect "Red Bear OS Phase 4 Wayland Runtime Check" expect "Red Bear OS Phase 4 Wayland Runtime Check"
expect "orbital-wayland" expect "redbear-validation-session"
expect "wayland-session" expect "wayland-session"
expect "smallvil"
expect "/home/root/.qt6-bootstrap-minimal.ok" expect "/home/root/.qt6-bootstrap-minimal.ok"
expect "/home/root/.qt6-plugin-minimal.ok" expect "/home/root/.qt6-plugin-minimal.ok"
expect "/home/root/.qt6-wayland-smoke-minimal.ok" expect "/home/root/.qt6-wayland-smoke-minimal.ok"
+2 -3
View File
@@ -18,9 +18,8 @@ require_command() {
fi 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 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-wayland-smoke "qt6-wayland-smoke is installed"
require_command qt6-bootstrap-check "qt6-bootstrap-check is installed" require_command qt6-bootstrap-check "qt6-bootstrap-check is installed"
require_command qt6-plugin-check "qt6-plugin-check is installed" require_command qt6-plugin-check "qt6-plugin-check is installed"
@@ -54,7 +53,7 @@ fi
echo echo
echo "=== Phase 4 launch surface ===" 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 "Run this script inside the guest, or use redbear-phase4-wayland-check as the canonical validator."
echo echo
echo "=== Test Complete ===" echo "=== Test Complete ==="
+3 -3
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/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 set -euo pipefail
@@ -27,7 +27,7 @@ usage() {
cat <<'USAGE' cat <<'USAGE'
Usage: test-phase6-kde-qemu.sh [--check] [extra qemu args...] 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 USAGE
} }
@@ -112,7 +112,7 @@ send "password\r"
expect "Type 'help' for available commands." expect "Type 'help' for available commands."
send "redbear-phase6-kde-check\r" send "redbear-phase6-kde-check\r"
expect "Red Bear OS Phase 6 KDE Runtime Check" expect "Red Bear OS Phase 6 KDE Runtime Check"
expect "orbital-kde" expect "redbear-kde-session"
expect "kwin_wayland" expect "kwin_wayland"
expect { expect {
"PHASE6_UPOWER_ENUMERATE=ok" {} "PHASE6_UPOWER_ENUMERATE=ok" {}
+108
View File
@@ -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()