Commit a1e7de32 authored by Paolo Bonzini's avatar Paolo Bonzini
Browse files

x86: eventinj: fix inline assembly and make it more robust to new compilers



Using assembler trampolines instead of && fixes NMI IRET test.

There is another bug, however.  Returns to the same privilege level do
not pop SS:RSP on 32-bit, so the nested NMI underflowed the stack at "s".
This is fixed in the new code.  The new code doesn't set up SS:RSP and
instead leaves some space for the nested NMI handler on the alternate
stack.  The old stack pointer is kept and restored when the nested
handler returns.

Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 309ca07b
......@@ -59,17 +59,25 @@ static volatile int test_count;
ulong stack_phys;
void *stack_va;
static void pf_tss(void)
void do_pf_tss(void)
{
start:
printf("PF running\n");
install_pte(phys_to_virt(read_cr3()), 1, stack_va,
stack_phys | PTE_PRESENT | PTE_WRITE, 0);
invlpg(stack_va);
asm volatile ("iret");
goto start;
}
extern void pf_tss(void);
asm (
"pf_tss: \n\t"
"call do_pf_tss \n\t"
"add $"S", %"R "sp\n\t" // discard error code
"iret"W" \n\t"
"jmp pf_tss\n\t"
);
static void of_isr(struct ex_regs *r)
{
printf("OF isr running\n");
......@@ -114,39 +122,51 @@ static void nmi_isr(struct ex_regs *r)
printf("After nested NMI to self\n");
}
unsigned long after_iret_addr;
unsigned long *iret_stack;
static void nested_nmi_iret_isr(struct ex_regs *r)
{
printf("Nested NMI isr running rip=%x\n", r->rip);
if (r->rip == after_iret_addr)
if (r->rip == iret_stack[-3])
test_count++;
}
extern void do_iret(ulong phys_stack, void *virt_stack);
// Return to same privilege level won't pop SS or SP, so
// save it in RDX while we run on the nested stack
asm("do_iret:"
#ifdef __x86_64__
"mov %rdi, %rax \n\t" // phys_stack
"mov %rsi, %rdx \n\t" // virt_stack
#else
"mov 4(%esp), %eax \n\t" // phys_stack
"mov 8(%esp), %edx \n\t" // virt_stack
#endif
"xchg %"R "dx, %"R "sp \n\t" // point to new stack
"pushf"W" \n\t"
"mov %cs, %ecx \n\t"
"push"W" %"R "cx \n\t"
"push"W" $1f \n\t"
"outl %eax, $0xe4 \n\t" // flush page
"iret"W" \n\t"
"1: xchg %"R "dx, %"R "sp \n\t" // point to old stack
"ret\n\t"
);
static void nmi_iret_isr(struct ex_regs *r)
{
unsigned long *s = alloc_page();
test_count++;
printf("NMI isr running %p stack %p\n", &&after_iret, s);
printf("NMI isr running stack %p\n", s);
handle_exception(2, nested_nmi_iret_isr);
printf("Sending nested NMI to self\n");
apic_self_nmi();
printf("After nested NMI to self\n");
s[4] = read_ss();
s[3] = 0; /* rsp */
s[2] = read_rflags();
s[1] = read_cs();
s[0] = after_iret_addr = (unsigned long)&&after_iret;
asm ("mov %%" R "sp, %0\n\t"
"mov %1, %%" R "sp\n\t"
"outl %2, $0xe4\n\t" /* flush stack page */
#ifdef __x86_64__
"iretq\n\t"
#else
"iretl\n\t"
#endif
: "=m"(s[3]) : "rm"(&s[0]), "a"((unsigned int)virt_to_phys(s)) : "memory");
after_iret:
iret_stack = &s[128];
do_iret(virt_to_phys(s), iret_stack);
printf("After iret\n");
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment