3

I am writing a debugger based on Intel VT.

As the iret instruction's performance in vmx-guest is changed while NMI-Exiting=1. So I should handle NMI in the vmx-host myself,otherwise,guest will have nmi reentrant bugs.

I checked the Intel manual :

While an NMI interrupt handler is executing, the processor disables additional calls to the NMI handler until the next IRET instruction is executed. This blocking of subse-quent NMIs prevents stacking up calls to the NMI handler.

So I am trying to simulate a iret in the vmx-host myself. the CPL remains ring0 and keep stack and code segment no change.

I write a sample code below,it was after vmx-exit caused by NMI:

asm volatile(
    "pushfq \n\t"
    "mov %%cs.%%ax \n\t"
    "push  %%rax\n\t"
    "mov $._restart_code,%%rax \n\t"
    "push %%rax \n\t"
    "iret \n\t"/*manully iret in the host before vmx-entry.*/
    "._restart_code:"
    "nop":);

Anyone can show some guides?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
hellolwq
  • 531
  • 3
  • 7
  • 18
  • 4
    That sounds like you are trying to work around something. If you explain what you are trying to achieve, somebody might know existing solution. – Jan Hudec Aug 01 '12 at 09:54
  • What (how and where) should trigger your interrupt? – GJ. Aug 02 '12 at 06:48
  • I added a comment on the sample,wish that will be clear now.thanks for your help! – hellolwq Aug 02 '12 at 07:19
  • 2
    An `iret` does a few things. The two that I can think of off the top of my head are: (1) a far transfer back to the original code; (2) restore eflags; (3) unblocking NMI. There are probably other items as well. Are you trying to simulate all of these in the host, or only the NMI unblocking part? – Nathan Fellman Aug 08 '12 at 13:24
  • I'm confused. If you're trying to clear the "Blocking by NMI" flag at bit 3 of the guest's Interruptibility state (in the guest's Guest State Area); then doing an IRET in the host won't achieve anything (it clears the host's "Blocking by NMI" flag and not the guest's). – Brendan Jul 23 '22 at 06:23

1 Answers1

1

Looks like your code is missing pushing SS and RSP. Here is my code for both i386 and x86_64: https://github.com/lxylxy123456/uberxmhf/blob/6b56acef71528b29e503ec66a2d305ba1b0c65f9/xmhf/src/xmhf-core/xmhf-runtime/xmhf-smpguest/arch/x86/vmx/smpg-x86vmx.c#L500

void xmhf_smpguest_arch_x86vmx_unblock_nmi(void) {
#ifdef __AMD64__
    asm volatile (
        "movq    %%rsp, %%rsi   \r\n"
        "xorq    %%rax, %%rax   \r\n"
        "movw    %%ss, %%ax     \r\n"
        "pushq   %%rax          \r\n"
        "pushq   %%rsi          \r\n"
        "pushfq                 \r\n"
        "xorq    %%rax, %%rax   \r\n"
        "movw    %%cs, %%ax     \r\n"
        "pushq   %%rax          \r\n"
        "pushq   $1f            \r\n"
        "iretq                  \r\n"
        "1: nop                 \r\n"
        : // no output
        : // no input
        : "%rax", "%rsi", "cc", "memory");
#elif defined(__I386__)
    asm volatile (
        "pushfl                 \r\n"
        "xorl    %%eax, %%eax   \r\n"
        "movw    %%cs, %%ax     \r\n"
        "pushl   %%eax          \r\n"
        "pushl   $1f            \r\n"
        "iretl                  \r\n"
        "1: nop                 \r\n"
        : // no output
        : // no input
        : "%eax", "cc", "memory");
#else /* !defined(__I386__) && !defined(__AMD64__) */
    #error "Unsupported Arch"
#endif /* !defined(__I386__) && !defined(__AMD64__) */
}
Eric Stdlib
  • 1,292
  • 1
  • 18
  • 32
  • Note that this is only safe for x86-64 System V if compiled with `-mno-red-zone`, since there's no way to tell the compiler you want to clobber the red-zone, and this doesn't skip 128 bytes before pushing. But that's probably ok because kernel code has to be compile that way. (But you can iret in user-space.) – Peter Cordes Jul 23 '22 at 05:48
  • Does `iret` care if the `cs` and `ss` it pops are correctly zero-extended? You should just be able to `movl %%ss, %%eax` to get a `mov` with no operand-size prefix, which will zero-extend on non-ancient CPUs (ppro and later, and all x86-64, [at least from Intel](https://www.felixcloutier.com/x86/mov#description)). (And in 32-bit mode you can `pushl %cs`.) 64-bit mode can avoid clobbering RSI by doing `lea 8(%rsp), %rax` *after* pushing a copy of SS. – Peter Cordes Jul 23 '22 at 05:55
  • Also, the `nop` seems pointless. You can have a label at the end of your asm template. The 64-bit version might want to use a RIP-relative LEA to reference it, although in position-dependent code, using the absolute address as a 32-bit sign-extended immediate (like you're doing) is a good choice for a high-half kernel, loaded in the top 2GiB of virtual address space. – Peter Cordes Jul 23 '22 at 05:56