x86: Prevent NMIs from nesting
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 19 Jul 2016 19:38:38 +0000 (15:38 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Fri, 29 Jul 2016 21:43:07 +0000 (17:43 -0400)
commit2248669bff4d519933b8241d2697789a5de523bc
tree2b55d8921d490a7777f8f4e208767be9e8d895ee
parent2d495bb5618abb468c54d95a7947b855c3074e84
x86: Prevent NMIs from nesting

If an NMI faults for any reason and then does an iret, the iret will clear
whatever protection hardware offers that prevents another NMI from nesting.
Basically, hardware will delay future NMIs until an iret.  The fault
handler's iret counts.

We get around this by having two stacks for NMIs.  One is for the real NMI
entry.  The second (the one in pcpui) is for the work done in NMI context.
This is the work that could get interrupted.  In that case, the NMI handler
makes sure the worker will do_nmi_work() again.

The main NMI handler and the bottom half work together such that a
concurrent NMI triggers a repeat of do_nmi_work().  The trickiness comes
when the bottom half tries to exit (by popping back to the original TF) and
an NMI happens at the same time.

The most error-prone and least-used part of this is how we handle that
scenario: the NMI handler notices the bottom half was trying to exit and
moves it to an alternate section of code that will return instead of
popping.  We can get away with this because there is a single instruction
that commits the "pop" of the TF and leaves the worker stack: iret.  It's
sort of like a restartable sequence, but instead of restarting, we undo the
operation.  That was relatively simple.  The harder part is writing the
nmi_try_to_pop, which is basically another NMI entry and exit under
slightly different circumstances.

I tested this a little.  I put a few direct jumps from within the OK case
to within the FAIL case.  I also put a "1: jmp 1b" loop in the OK case and
two nops in the FAIL case, added a breakpoint to do_nmi_work, and sent
another NMI to trigger the TF-hacking code.  Seems to work, for now.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/arch/x86/smp_boot.c
kern/arch/x86/trap.c
kern/arch/x86/trapentry64.S
kern/include/smp.h