From 01f8cfe0b484d6bc17e089b853a1b33df05bc97d Mon Sep 17 00:00:00 2001 From: Callan Barrett Date: Fri, 26 Jun 2026 10:06:29 +0800 Subject: [PATCH 1/2] fix(zaparoo): shut down frontend before FPGA reconfig to stop launch freeze MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Launching a core from the launcher frontend could hard-freeze the device (silent black screen, reboot required) — reliably reproduced on atari5200 /atari800. The frontend process stays alive holding mmaps into /dev/fb0, the HPS framebuffer the FPGA scans out over the f2sdram bridge. fpga_load_rbf calls do_bridge(0) to reset that bridge while the frontend is still mapped to it, deadlocking the AXI bus mid-reconfiguration. Upstream has no frontend, so it never hits this. Move the frontend teardown ahead of reconfiguration: call alt_launcher_shutdown() at the top of fpga_load_rbf so the frontend (and its framebuffer) is gone before do_bridge(0)/socfpga_load. The call is idempotent, so app_restart's existing call is left untouched. Also harden the frontend VT takeover: replace the unbounded video_chvt() (VT_WAITACTIVE blocks forever if the frontend stalls bringing up video) with a bounded VT_ACTIVATE + VT_GETSTATE poll, and defer the takeover until the FPGA is ready. This fixes a separate poll-cothread wedge during menu+frontend bring-up after a 15kHz core. --- fpga_io.cpp | 4 ++++ support/zaparoo/alt_launcher.cpp | 26 +++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/fpga_io.cpp b/fpga_io.cpp index 45c91f51b..994f9e8d2 100644 --- a/fpga_io.cpp +++ b/fpga_io.cpp @@ -427,6 +427,10 @@ static int make_env(const char *name, const char *cfg) int fpga_load_rbf(const char *name, const char *cfg, const char *xml) { + // Tear down the launcher frontend (and its HPS framebuffer mmaps) before any + // FPGA reconfiguration. A live frontend scanning out /dev/fb0 over the f2sdram + // bridge deadlocks the AXI bus when do_bridge(0) resets it during load. + alt_launcher_shutdown(); OsdDisable(); static char path[1024]; int ret = 0; diff --git a/support/zaparoo/alt_launcher.cpp b/support/zaparoo/alt_launcher.cpp index a9529b24e..9be1ddd59 100644 --- a/support/zaparoo/alt_launcher.cpp +++ b/support/zaparoo/alt_launcher.cpp @@ -17,6 +17,7 @@ #include #include "cfg.h" #include "file_io.h" +#include "fpga_io.h" #include "hardware.h" #include "input.h" #include "menu.h" @@ -472,10 +473,33 @@ static void exec_launcher_child(const char *path) _exit(1); } +// Bounded replacement for video_chvt(): its VT_WAITACTIVE blocks forever if the +// frontend stalls bringing up video, which would wedge the poll cothread. +static void switch_to_launcher_vt(void) +{ + int fd = open("/dev/tty0", O_RDONLY | O_CLOEXEC); + if (fd < 0) return; + + if (ioctl(fd, VT_ACTIVATE, s_vt)) printf("alt_launcher: VT_ACTIVATE fails\n"); + + for (int i = 0; i < 50; i++) + { + struct vt_stat st; + if (!ioctl(fd, VT_GETSTATE, &st) && st.v_active == s_vt) break; + usleep(10000); + } + + close(fd); +} + static void finalize_spawn(void) { + // Defer the VT/fb takeover until the FPGA/HDMI has settled; s_tty_deadline + // stays armed so this retries on a later alt_launcher_poll pass. + if (!is_fpga_ready(1)) return; + s_tty_deadline = 0; - video_chvt(s_vt); + switch_to_launcher_vt(); if (!s_native_crt) video_fb_enable(1); else From a2eacf81c1e4c2403eaac2b32c2a0ea98a1d7739 Mon Sep 17 00:00:00 2001 From: Callan Barrett Date: Fri, 26 Jun 2026 10:13:21 +0800 Subject: [PATCH 2/2] fix(zaparoo): yield to scheduler instead of usleep during VT wait switch_to_launcher_vt() ran in the poll cothread. usleep() in libco's cooperative single-thread model sleeps the whole process, so the bounded VT wait stalled both cothreads (no UI/OSD, no poll) for up to 500ms. Replace the fixed usleep retry loop with a wall-clock deadline (GetTimer/CheckTimer) plus scheduler_yield(), keeping the same bound while staying cooperative. --- support/zaparoo/alt_launcher.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/support/zaparoo/alt_launcher.cpp b/support/zaparoo/alt_launcher.cpp index 9be1ddd59..3d03ced4b 100644 --- a/support/zaparoo/alt_launcher.cpp +++ b/support/zaparoo/alt_launcher.cpp @@ -21,6 +21,7 @@ #include "hardware.h" #include "input.h" #include "menu.h" +#include "scheduler.h" #include "shmem.h" #include "user_io.h" #include "video.h" @@ -482,11 +483,15 @@ static void switch_to_launcher_vt(void) if (ioctl(fd, VT_ACTIVATE, s_vt)) printf("alt_launcher: VT_ACTIVATE fails\n"); - for (int i = 0; i < 50; i++) + // Yield to the scheduler rather than usleep() so the poll cothread stays + // cooperative while we wait (bounded) for the VT to become active. + unsigned long deadline = GetTimer(500); + for (;;) { struct vt_stat st; if (!ioctl(fd, VT_GETSTATE, &st) && st.v_active == s_vt) break; - usleep(10000); + if (CheckTimer(deadline)) break; + scheduler_yield(); } close(fd);