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.
git clone https://github.com/samedayhurt/bc250-buddy.git
cd bc250-buddy
./install.shOr 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 orderUnattended (review the scripts first):
BC250_ASSUME_YES=1 ./install.sh all| # | 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 |
| # | 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.
| # | 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.
| 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 |
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.
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.
- Grab a custom BIOS from the BC-250 Discord (EN) or Telegram (UA/RU).
- Copy it to a FAT32 USB stick.
- Boot to BIOS (press
DELduring POST). - Flash via the built-in updater.
- 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.
- Re-enter BIOS and configure.
Reference: OldLamer Part IV
- Official image: https://bazzite.gg — pick
bazzite-deckfor 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 rebootThen clone this repo and run ./install.sh.
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 = 80Reference: 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.swappinessReference: 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 = 90Adds 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.
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.conf— stops 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, becausenmcli … powersave 2does not survive a reboot under the iwd backend/etc/sysctl.d/90-wifi-tune.conf—fq_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.
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 sunshineEnables 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 tailscaleArms 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 watchdogOnly recovers a kernel wedge. A VRM or PSU trip leaves the board off and no software can help.
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 cuThe 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 cuThe 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./install.sh verifyReports 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.
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.shIf the system refuses to boot after an overclock, pick the previous rpm-ostree deployment from the GRUB menu.
Reference: OldLamer — "Breaking news - Multi Frame Generation via Decky Loader"
- Switch to Desktop Mode.
- Install Decky Loader: https://decky.xyz.
- Back in Game Mode, open the Quick Access Menu → Decky → Plugin Store.
- Install DLSS Enabler and enable per-game from the Decky sidebar.
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).
- Game crashing (OOM) — confirm zswap is active:
cat /sys/module/zswap/parameters/enabled(should beY) andswapon --show. - GPU stuck at low clocks —
systemctl restart cyan-skillfish-governor-smu, then re-checkcat /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 NetworkManagerfor reconnect loops; try the 2.4 GHz band; ultimately, plug in Ethernet. - Random hard lockups with no shutdown sequence —
journalctl -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 black —
encoderisn'tsoftware, orcap_sys_adminis 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.
- OldLamer — YouTube series that all of this is based on: https://www.youtube.com/@OldLamer
- BC-250 Telegram (UA/RU): https://t.me/BC250public
- BC-250 Discord (EN): https://discord.com/invite/8eZfFWhczz
- bc250-collective GitHub org: https://github.com/bc250-collective
- filippor/cyan-skillfish-governor: https://github.com/filippor/cyan-skillfish-governor/tree/smu
PRs welcome — especially chip-tier overclock tables and notes from revisions I haven't tested.
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.