Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

bc250-buddy

Scripted setup for turning an ASRock BC-250 (Xbox Series S APU board) into a working, overclocked Steam Machine on Bazzite. Built from the excellent OldLamer YouTube series (Parts I–XV) and tested on bazzite-deck running kernel 6.17.x.

It also sets the box up as a Sunshine/Moonlight streaming host over Tailscale — see STREAMING.md for that half, including the Wi-Fi bug that made streaming stutter every 65 seconds and eventually hard-locked the machine.

Everything here is idempotent, per-step confirmable, and comes with revert scripts.


Quick start

git clone https://github.com/samedayhurt/bc250-buddy.git
cd bc250-buddy
./install.sh

Or pick a single module:

./install.sh gpu        # GPU governor
./install.sh mem        # zswap + swapfile
./install.sh cpu        # CPU overclock
./install.sh mit        # mitigations=off
./install.sh wifi       # WiFi tuning + roam-scan / power-save fixes
./install.sh sunshine   # Sunshine host (software encode, KMS capture)
./install.sh tailscale  # Tailscale, for streaming from outside the LAN
./install.sh watchdog   # hardware watchdog + crash forensics heartbeat
./install.sh cu         # GPU compute-unit unlock (experimental, opt-in)
./install.sh verify     # status report

./install.sh perf       # performance set: mem, gpu, cpu, mit
./install.sh stream     # streaming set:   wifi, sunshine, tailscale, watchdog
./install.sh all        # everything, in order

Unattended (review the scripts first):

BC250_ASSUME_YES=1 ./install.sh all

What this does

Performance (./install.sh perf)

# Module Effect
1 GPU governor (cyan-skillfish-governor-smu) GPU now scales 350–2230 MHz instead of being stuck at ~1000 MHz
2 zswap + 32 GB Btrfs swapfile Stops OOM crashes during shader compilation on the shared 16 GB GDDR6
3 CPU overclock (bc250_smu_oc) 3493 MHz → 3842 MHz (+10%) at 1150 mV, thermally safe
4 mitigations=off kernel arg +3–5% FPS in CPU-bound games

Streaming (./install.sh stream)

# Module Effect
5 WiFi tuning Stops iwd roam-scanning (the ~65 s stutter), kills power-save at all three layers, fq_codel + anti-bufferbloat sysctls
6 Sunshine host Forces software H.264 — the BC-250 has no hardware encoder — verifies cap_sys_admin for KMS capture, enables the service
7 Tailscale Stream from anywhere with no port forwarding, plus direct-vs-DERP-relay diagnosis
8 Watchdog + forensics Turns a hard lockup into a 60 s auto-reboot; fsync'd heartbeat so you can tell "the box died" from "the network died"

Full write-up, client setup for Android/Fire TV/desktop/Apple, and a triage runbook: STREAMING.md.

Experimental, opt-in only

# Module Effect
9 GPU compute-unit (WGP) unlock Routes dispatch to all 20 WGP pairs (40 CUs) instead of the 24 CUs amdgpu enumerates

Not included in perf or all — run ./install.sh cu explicitly. It writes low-level GPU registers, raises power draw on a board whose most common failure mode is an underfed 12 V rail, and comes with no benchmark. See module 9.

Measured before/after (BC-250 #1, bazzite-deck)

Metric Before After
CPU clock 3,493 MHz 3,842 MHz
GPU clock range 350–1000 MHz (stuck) 350–2230 MHz (dynamic)
Swap backing zRAM only (~1.8 GB) zswap + 32 GB disk
CPU mitigations On Off
WiFi latency (rtw89 USB) 1,500–9,500 ms 6–200 ms
WiFi power-save idle ping 124 ms avg / 70 ms jitter 63 ms avg / 1.8 ms jitter
iwd roam-scans per boot one every ~65 s 0
Stream stutter every 1–2 min gone
Recovery from a hard lock manual power-cycle ~60 s auto-reboot

Hardware prerequisites

Reference: OldLamer Part VII — PSU and Cooling

  • PSU: 300 W minimum 12 V supply. Best compact option is the Mean Well LOP-300-12. Budget option: a used server PSU with a 12 V breakout board.
  • Wiring: 16 AWG minimum. 22 AWG will melt — this is the single most common way to brick a BC-250.
  • Cooling: add a 120 mm fan in blow-through config. Replace the thermal pads (pad sizes vary by revision). Target under 70 °C in AAA games at stock before you try to overclock.
  • Storage: NVMe M.2 (there's only one slot and it holds the OS).
  • Networking: wired Ethernet strongly preferred. If you must use WiFi, a USB 3.0 adapter with a MediaTek MT7921AU is the best-supported on Linux. Cheap Realtek rtw89 USB dongles will work but are flaky — module 5 helps them.

BIOS

Reference: OldLamer Part V — BIOS Update

Stock BIOS (P3.00) hard-splits 12 GB GPU / 4 GB CPU. A custom BIOS from the community unlocks dynamic VRAM allocation up to 8 GB CPU-side — you want this.

  1. Grab a custom BIOS from the BC-250 Discord (EN) or Telegram (UA/RU).
  2. Copy it to a FAT32 USB stick.
  3. Boot to BIOS (press DEL during POST).
  4. Flash via the built-in updater.
  5. After flashing, CMOS reset — jumper for 5 s or pull the BIOS battery for 30 s. If you skip this you'll get the fixed VRAM split back.
  6. Re-enter BIOS and configure.

Installing Bazzite

Reference: OldLamer Part IV

  • Official image: https://bazzite.gg — pick bazzite-deck for the Steam Deck-style game mode.
  • If the official image refuses to boot on your revision, the Discord/Telegram keeps a known-working mirror.
# After first boot, update to current:
sudo rpm-ostree upgrade
sudo systemctl reboot

Then clone this repo and run ./install.sh.


Module details

1. GPU governor

Replaces the older oberon-governor with cyan-skillfish-governor-smu, which matches how the Xbox Series S actually drives this silicon.

Layered package: cyan-skillfish-governor-smu (via COPR filippor/bazzite).

Tune the frequency/voltage curve in /etc/cyan-skillfish-governor-smu/config.toml. A safe starting curve:

safe_points = [
    [1000, 800], [1200, 850], [1400, 900], [1600, 950],
    [1800, 1000], [2000, 1050], [2200, 1100], [2400, 1150],
]
temp_throttle = 85
temp_recovery = 80
load_target_low = 65
load_target_high = 80

2. Memory — zswap + swapfile

Reference: OldLamer Part XIV

The 16 GB is shared with the GPU, so any heavy DXVK/VKD3D shader build eats the box. zswap with a 32 GB disk-backed swapfile absorbs it cleanly. Expect ~2 GB swap in use at idle — that's healthy on this hardware, not a leak.

Configurable env vars:

BC250_SWAP_SIZE=32G    # swapfile size
BC250_SWAPPINESS=120   # vm.swappiness

3. CPU overclock

Reference: OldLamer Part VIII

Applies via bc250_smu_oc. Defaults are the verified-stable point on an average chip:

Variable Default Meaning
BC250_OC_FREQ 3850 MHz
BC250_OC_VID 1150 mV (hard cap: never exceed 1300)
BC250_OC_SCALE -35 scale factor
BC250_OC_TMAX 90 °C emergency cap

The script runs bc250-detect as a built-in stress test before applying. If it crashes: raise BC250_OC_VID by 25 mV or lower BC250_OC_FREQ by 100 MHz and re-run.

Config written to /etc/bc250-smu-oc.conf:

[overclock]
frequency = 3850
scale = -35
max_temperature = 90

4. Disable CPU mitigations

Adds mitigations=off to kernel args. Worth a few percent FPS in CPU-bound games. Only do this on a dedicated gaming box — don't ship a workstation this way.

5. WiFi optimization

Only useful if you can't run Ethernet — but if you are on WiFi and streaming, this module is not optional. The files it writes:

  • /etc/iwd/main.confstops roam-scanning. iwd scans every ~65 s even on a stationary box with one AP, which is exactly the "stutters every minute or two" complaint, and on USB dongles it eventually wedges the radio and hard-locks the machine. See STREAMING.md § The 65-second stutter.
  • /etc/modprobe.d/rtw89-fix.conf — disables the rtw89 driver's internal PS state machine (a separate layer from mac80211 power-save)
  • /etc/NetworkManager/conf.d/wifi-fix.conf — disables NM WiFi power save
  • /etc/NetworkManager/dispatcher.d/99-wifi-powersave-off — re-asserts power-save off on every link-up, because nmcli … powersave 2 does not survive a reboot under the iwd backend
  • /etc/sysctl.d/90-wifi-tune.conffq_codel, ECN, TCP Fast Open

Note: USB WiFi adapters frequently work better on the 2.4 GHz band than 5 GHz due to the short integrated antennas. YMMV.

6. Sunshine host

Forces software H.264 encoding into ~/.config/sunshine/sunshine.conf (existing config is backed up first), because this GPU has no video-encode block — there are no VA-API encode entrypoints and anything but encoder = software gives you a black stream. Also verifies cap_sys_admin on the binary (needed for KMS capture), checks that a display output is actually connected, and enables the service.

Sunshine ships in the Bazzite image; this does not install anything.

BC250_SW_PRESET=superfast BC250_SW_TUNE=zerolatency BC250_MIN_THREADS=4 \
    ./install.sh sunshine

7. Tailscale

Enables tailscaled (also already in the Bazzite image — don't layer it), joins your tailnet, adds tailscale0 to firewalld's trusted zone, and reports whether peers get a direct UDP path or a DERP relay. Relayed peers connect and then stutter, which is the most commonly misdiagnosed remote-streaming failure.

BC250_TS_HOSTNAME=bc250 ./install.sh tailscale

8. Watchdog + crash forensics

Arms the SoC's SP5100 TCO timer via systemd so a hard lockup becomes a ~60 s auto-reboot instead of a dead box until someone walks over to it. Optionally installs bc250-hb.service, an fsync'd heartbeat that records the exact moment the machine stopped executing — the only way to tell "the box died" apart from "the network died" after the fact.

BC250_WD_RUNTIME=60 BC250_WD_HEARTBEAT=yes ./install.sh watchdog

Only recovers a kernel wedge. A VRM or PSU trip leaves the board off and no software can help.

9. GPU compute-unit (WGP) unlock

Experimental. Opt-in only — not part of perf or all.

The die has 40 CUs in 20 WGP pairs across 4 shader arrays, and amdgpu enumerates 24. The community bc250-cu-live-manager script uses umr to rewrite the gfx1013 registers (mmCC_GC_SHADER_ARRAY_CONFIG, mmSPI_PG_ENABLE_STATIC_WGP_MASK, mmRLC_PG_ALWAYS_ON_WGP_MASK) that control CU enumeration and WGP dispatch.

Stock layout is 6 active CUs per row — WGP0–2 (CU0–5) enabled, WGP3–4 (CU6–9) factory-disabled across all four SE/SH rows. That's the 24. A full apply routes 10 per row.

./install.sh cu

The manager script is vendored at third-party/bc250-cu-live-manager.sh and pinned by checksum. It carries no author, license, or upstream URL, so it sits outside this repo's MIT scope — provenance and the audit done before vendoring it are in third-party/NOTICE.md. To use your own copy instead:

BC250_CU_MANAGER=/path/to/your/copy.sh ./install.sh cu

The module layers umr if needed, verifies the checksum, installs the script to /usr/local/bin (which is /var/usrlocal on Fedora Atomic, so it survives image updates), applies the profile, and installs the oneshot boot service that re-applies it after every boot.

Be honest about what you get. After a full apply the dashboard reports SPI total : 40/40 CUs, but also Driver lock: 24/40 CUs active and active_cu_number=24. The extra WGPs are routed at the SPI level only; amdgpu's own topology is unchanged. Making the driver enumerate all 40 needs the bc250_cc_write_mode module parameter, which current kernels report as not exposed. So this is a dispatch-routing change, not a full unlock, and no A/B benchmark accompanies it.

The cost is measurable even if the gain isn't: PPT was observed peaking at 91 W with it active. Given the PSU warnings above, that is real headroom being spent — and a plausible contributor to a VRM trip, which the watchdog cannot recover from.

sudo bc250-cu-live-manager status     # dashboard
sensors | grep -iE 'PPT|Tctl'         # watch this after enabling

Verification

./install.sh verify

Reports which modules are live and prints GPU clock states, CPU average MHz, machine-check error counts by core, swap usage, kargs, WiFi link quality and roam-scan/probe-failure counts, Sunshine's encoder and capability state, the Tailscale path, the watchdog state, and whether the previous boot ended in a clean shutdown or a hard lock.

Reverting

Every module has a revert script. Use the interactive menu or:

bash scripts/revert/revert-gpu-governor.sh
bash scripts/revert/revert-memory-zswap.sh
bash scripts/revert/revert-cpu-overclock.sh
bash scripts/revert/revert-mitigations.sh
bash scripts/revert/revert-wifi.sh
bash scripts/revert/revert-sunshine.sh
bash scripts/revert/revert-tailscale.sh
bash scripts/revert/revert-watchdog.sh
bash scripts/revert/revert-cu-unlock.sh

If the system refuses to boot after an overclock, pick the previous rpm-ostree deployment from the GRUB menu.


Frame generation (manual, after scripted setup)

Reference: OldLamer — "Breaking news - Multi Frame Generation via Decky Loader"

  1. Switch to Desktop Mode.
  2. Install Decky Loader: https://decky.xyz.
  3. Back in Game Mode, open the Quick Access Menu → Decky → Plugin Store.
  4. Install DLSS Enabler and enable per-game from the Decky sidebar.

Game-specific device-ID spoofing

Some games don't recognize the BC-250 GPU. In Steam launch options:

DXVK_CONFIG="dxgi.customDeviceId=731F dxgi.customVendorId=1002" %command%

Known-affected: Crimson Desert (may already be fixed in Proton Experimental Bleeding Edge).


Troubleshooting

  • Game crashing (OOM) — confirm zswap is active: cat /sys/module/zswap/parameters/enabled (should be Y) and swapon --show.
  • GPU stuck at low clockssystemctl restart cyan-skillfish-governor-smu, then re-check cat /sys/class/drm/card*/device/pp_dpm_sclk.
  • Boot failure after overclock — GRUB → previous deployment, then sudo systemctl disable bc250-smu-oc.
  • WiFi dropouts — check journalctl -u NetworkManager for reconnect loops; try the 2.4 GHz band; ultimately, plug in Ethernet.
  • Random hard lockups with no shutdown sequencejournalctl -b -1 | tail -20. If the log just stops, see STREAMING.md §8: the usual culprits are a wedged USB WiFi dongle and, second, corrected machine-check errors from too aggressive an undervolt.
  • Stream stutters every minute or two — iwd roam-scanning. Run ./install.sh wifi.
  • Moonlight connects but the picture is blackencoder isn't software, or cap_sys_admin is missing from the Sunshine binary, or nothing is plugged into the video output. Run ./install.sh sunshine.
  • Stream is fine on the LAN, unplayable remotely — you're on a DERP relay. tailscale ping <host> from the client; see STREAMING.md §4.3.

Community & credit

PRs welcome — especially chip-tier overclock tables and notes from revisions I haven't tested.

License

MIT. See LICENSE.

Exception: third-party/ is not covered by that license. It vendors a community tool with no stated author or license — see third-party/NOTICE.md. Nothing else in the repo depends on it; rm -rf third-party/ is safe.

The scripts modify kernel args, layer packages, and overclock hardware. Use at your own risk. I've run every module on at least one BC-250 without incident, but your board, PSU, and cooling are not mine.

About

Scripted Bazzite setup for ASRock BC-250 Steam Machines — GPU governor, zswap, CPU overclock, WiFi tuning

Resources

Stars

11 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages