The Easiest Way to Reset an i386/x86_64 System

Try this in kernel mode:

uint64_t null_idtr = 0;
asm("xor %%eax, %%eax; lidt %0; int3" :: "m" (null_idtr));

This can be quite helpful when doing operating system development on an i386/x86_64 system. You can use this for the regular restart case or when a kernel panic is supposed to restart immediately and you cannot make any assumptions on what is still working in the system.

You can also use this for debugging very low-level code if you don’t have a serial port or even an LED to report the most basic information: First make sure your code is reached by putting the reset code there. Then remove it again and put this code in:

if (condition)
    reset();
else
    for(;;);

The system will either hang or reset, depending on the condition.

8 thoughts on “The Easiest Way to Reset an i386/x86_64 System”

  1. The infamous triple fault exception… I used this back in the old DOS days in a small TSR to reboot the machine in when you pressed a certain key combo. It even worked in cases where the regular ctrl-alt-del was caught by some other program :)

    -Darkstar

  2. xor %eax,%eax will zero eax register. I guess it’s faster than moving immediate data into register.

    Ed.

  3. I was wondering about that, too. And after consulting my old trusty ia32 assembly reference (actually the “Turbo Assembler 2.0 Reference manual”), I think it can indeed be left out: lidt works only on the memory reference given, and int3 definitely doesn’t need it….

    Maybe some side-effect that I’m missing?

  4. That trick is actually quite old. It was used by memory managers for DOS on 286 boxes back then: A reset was the only way to get the CPU back from protected mode to real mode and the “official” way to reset the CPU was too slow (it involved messing around with the keyboard controller, IIRC). The triple fault trick, however, was convenient and fast.

  5. The suggested code caused GP faults on a Intel core i5 processor, below is the C and disassembled code:


    __attribute__ ((__noreturn__))
    void ac_reset(void) {
    uint64_t null_idtr = 0;
    __asm__ volatile ("lidt %0; int3" :: "m" (null_idtr));

    // Loop with interrupt off if it doesn't work
    __asm__ volatile ("cli");
    while (1) {
    __asm__ volatile ("hlt");
    }
    }

    00000000001024b0 :
    1024b0: 48 83 ec 10 sub $0x10,%rsp
    1024b4: 48 c7 44 24 08 00 00 movq $0x0,0x8(%rsp)
    1024bb: 00 00
    1024bd: 0f 01 5c 24 08 lidt 0x8(%rsp)
    1024c2: cc int3
    1024c3: fa cli
    1024c4: 0f 1f 40 00 nopl 0x0(%rax)
    1024c8: f4 hlt
    1024c9: eb fd jmp 1024c8
    1024cb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)

    When I run the above code on my i5 the lidt instruction causes a continuous stream of GP fault’s (exception 13). In that handler I’m dumping the exception frame as well as the contents of the idt register. We see that the idt register is not null and that frame.ip is pointing at the lidt instruction:


    expt_13_undefined:
    frame->ip: 00000000001024bd
    frame->cs: 0000000000000008
    frame->flags: 0000000000210002
    frame->sp: 000000000010bfe0
    frame->ss: 10
    rsp: 000000000010be40
    error_code: 0000000000000000
    expt_undefined_counter: 1
    idt.limit=4095
    idt.address=0000000000105040
    expt_13_undefined:
    frame->ip: 00000000001024bd
    frame->cs: 0000000000000008
    frame->flags: 0000000000210002
    frame->sp: 000000000010bfe0
    frame->ss: 10
    rsp: 000000000010be40
    error_code: 0000000000000000
    expt_undefined_counter: 2
    idt.limit=4095
    idt.address=0000000000105040
    ...

    The fix I devised was to use struct desc_ptr and now the CPU resets instead of causing the GP fault, below is the C and disassebled code:


    __attribute__ ((__noreturn__))
    void ac_reset(void) {
    struct {
    uint16_t limit;
    uint64_t address;
    } null_idtr;

    null_idtr.limit = 0;
    null_idtr.address = 0;

    __asm__ volatile ("lidt %0; int3" :: "m" (null_idtr));

    while (1) {
    hlt();
    }
    }

    00000000001024b0 :
    1024b0: 48 83 ec 18 sub $0x18,%rsp
    1024b4: 31 c0 xor %eax,%eax
    1024b6: 66 89 04 24 mov %ax,(%rsp)
    1024ba: 48 c7 44 24 08 00 00 movq $0x0,0x8(%rsp)
    1024c1: 00 00
    1024c3: 0f 01 1c 24 lidt (%rsp)
    1024c7: cc int3
    1024c8: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
    1024cf: 00
    1024d0: f4 hlt
    1024d1: eb fd jmp 1024d0

Leave a Reply to bi -- International Journal of Inactivism Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.