Skip to content

Mac OS 9 (PowerPC): boot sometimes hangs at "Starting Up…" (screamer input DMA channel left ACTIVE with RUN cleared) #8033

Open

Description

@TinBane

Describe the issue

A Mac OS 9.2.1 VM (mac99,via=pmu, G3, 256 MB, sound on) sometimes stops during start-up. The "Starting Up…" progress bar freezes at about 10% and the VM never gets further. The CPU is spinning, not crashed: timer interrupts still run and the guest tick counter keeps advancing. It's intermittent; other boots of the same disk are fine.

What the guest is waiting for

Sampled over QMP during the hang:

  • info registers keeps landing in a 7-instruction loop in Apple's sound plugins ('nlib' resources -20919 I2SAudioPlugin and -20536 DAVAudioPlugin in the System file; the code is the same in both). The loop reads the DBDMA ChannelStatus register of channel 0x12, the screamer input channel (0x80008904), byte-swaps it, and repeats while ACTIVE (0x400) is set. It has no timeout.
  • xp /2wx 0x80008900 = 0x00040000 0x00040000. ChannelStatus is little-endian, so its value is 0x00000400: ACTIVE set, RUN clear. Real DBDMA hardware never reports that combination: clearing RUN stops the channel and drops ACTIVE.

Just before the loop, the plugin asks the running input program to stop by setting status bit S0 through ChannelControl (mask 0x0001 / value 0x0001; the channel program branches on S0 to a STOP), then waits for ACTIVE to drop.

Cause in UTM's QEMU

hw/audio/screamer.c, pmac_screamer_rx() (utm-edition):

/* FIXME: stop channel after updating with status to stop MacOS 9 freezing */
ch->regs[DBDMA_STATUS] &= ~RUN;
io->dma_end(io);

It clears RUN but leaves ACTIVE set. dbdma_end() only continues a channel that has both RUN and ACTIVE, so nothing clears ACTIVE again except a later ChannelControl write. If this rx runs after the driver's stop request (it runs from the DBDMA bottom half, so it can), the driver spins on ACTIVE forever. That would explain why it's intermittent.

Proposed fix (one line)

ch->regs[DBDMA_STATUS] &= ~(RUN | ACTIVE);

Testing so far, to be clear about what it does and doesn't show:

  • The hang was caught twice on one VM (2026-10-30 and 2026-10-31). Both times UTM had been open for a long time and was running the VM at macOS background priority (every QEMU thread at Darwin priority 4, boots taking 6–10 minutes instead of ~50 s). I suspect the slow, throttled guest widens the race window; I haven't reproduced the hang at normal priority.
  • I built utmapp/qemu utm-edition (QEMU 10.0.12) with and without the change and booted the same disk headless with UTM's arguments: 11 boots stock and 10 with the fix, all reached the Finder in 43–50 s. So the change causes no regression in my testing, but I haven't yet seen it prevent a hang, because the hang didn't recur in those runs.

A related gap, also in upstream QEMU: dbdma_control_write() in hw/misc/macio/mac_dbdma.c ignores the S7–S0 bits of ChannelControl's mask/value. On DBDMA hardware software can set those bits, and channel programs test them in WAIT/BRANCH/INTERRUPT conditions (QEMU's conditional_wait() and conditional_branch() read them, but software can never set them). Mac OS's sound plugins use this to stop the input channel. Not needed for the fix above.

Guest-side workaround I'm using meanwhile: a same-size patch to the two plugins that bounds the wait.

I'm happy to open a PR against utmapp/qemu for the one-line change if you'd like it, after testing it on a UTM build.

Configuration

  • UTM Version: 5.0.8 (bundled QEMU 10.0.12)
  • macOS Version: 26.7.2
  • Mac Chip: Apple M1 Pro
  • Guest: Mac OS 9.2.1, mac99,via=pmu, -cpu G3, -m 256, -audio coreaudio

Debug log
Not attached: the hang is intermittent and the evidence above was captured over QMP. I can turn on the debug log and try to catch another one if that would help.

The investigation and this write-up were done with AI assistance (Claude).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions