panic stops every other processor

A panic used to stop only the processor it happened on. The rest carried
on with a dead kernel: on the ASUS laptop the report flashed up and was
drawn over, and the fans stayed loud until the power button was held.

Now the panicking processor sends every other one an NMI, which arrives
even with interrupts off, and trap() halts a processor that takes one
while panic_stop is set -- first thing, before the canary check, since
that stack may be what broke. NMI gets its own per-CPU stack (IST 1):
for a few instructions at the top of syscall_entry the live stack is the
user's, at ring 0, and a fault pushing the frame there would be a double
fault on the same stack and a reset instead of a report.

A second processor that panics while one is already reporting halts
instead of drawing over it; the owner's own nested panic still gets
through.

Checked under QEMU with -smp 4: after a sysrq crash CPUs 1-3 sit in hlt
with IF clear and CPU 0 is in the report's blink loop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
Nexoniarz 2026-10-25 00:50:50 +02:00
commit 38c89b1a9a
5 changed files with 57 additions and 0 deletions

View file

@ -56,6 +56,14 @@ struct tssblk {
static struct tssblk tssblk[NCPU];
// The stack an NMI arrives on: IST slot 1, one per CPU. Without it an NMI runs
// on whatever stack was live, and for a few instructions at the top of
// syscall_entry that is the user's -- at ring 0, so the CPU would push the
// trap frame there, and a fault on that push is a double fault on the same
// stack. Panic stops the other processors with an NMI; it must not be the
// thing that resets the machine instead of the report.
static uint8 nmistack[NCPU][4096] __attribute__((aligned(16)));
// What each CPU currently has open. Kept per CPU rather than read back from the
// process, because switching *away* has to close what was opened and the
// process that opened it may be part-way through exit() by then.
@ -89,6 +97,7 @@ ioperm_tss(int cpu, uint64 *limit)
memset(b->map, 0xFF, sizeof(b->map));
b->tail = 0xFF;
b->ts.iomb = (uint16)__builtin_offsetof(struct tssblk, map);
b->ts.ist[0] = (uint64)(nmistack[cpu] + sizeof(nmistack[cpu]));
installed[cpu].n = 0;
// The limit is the offset of the last valid byte, and it has to include the