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.
What’s the purpose of the xor %eax,%eax?
You’re giving away all my secrets!
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
xor %eax,%eax will zero eax register. I guess it’s faster than moving immediate data into register.
Ed.
Eldito:
Um, what I wanted to know was why %eax needs to be zero in this case.
Won’t the following be enough, or did I miss something?
uint64_t null_idtr = 0;
asm(“lidt %0; int3” :: “m” (null_idtr));
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?
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.
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