Reboot loop when enabling interrupts with sti

1

Whenever I try to enable interrupts using the sti command, the emulator instantly reboots. I am executing the following code in protected mode on bootup:

void* idt = (void*) 0x00000;
struct {
    uint16_t length;
    void*    base;
} __attribute__((packed)) IDTR = {128, idt};

asm ("lidt %0" : : "m"(IDTR));
asm ("sti");

There is a brief period of time when the OS has booted up, and even accepts input, but then it reboots. Why does this happen and how do I fix it?

EDIT: When adding -d int to qemu, I get the following output:

SMM: enter
EAX=000000b5 EBX=00008acc ECX=00005678 EDX=000ece30
ESI=07fbdde6 EDI=000ece30 EBP=00006c38 ESP=00006c38
EIP=000f8acb EFL=00000016 [----AP-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS   [-WA]
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS   [-WA]
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS   [-WA]
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS   [-WA]
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS   [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT=     000f7070 00000037
IDT=     000f70ae 00000000
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000 
DR6=ffff0ff0 DR7=00000400
CCS=00000014 CCD=00006c24 CCO=EFLAGS  
EFER=0000000000000000
SMM: after RSM
EAX=000000b5 EBX=00008acc ECX=00005678 EDX=000ece30
ESI=07fbdde6 EDI=000ece30 EBP=00006c38 ESP=00006c38
EIP=00008acc EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =de00 000de000 ffffffff 00809300
CS =f000 000f0000 ffffffff 00809b00
SS =0000 00000000 ffffffff 00809300
DS =0000 00000000 ffffffff 00809300
FS =0000 00000000 ffffffff 00809300
GS =0000 00000000 ffffffff 00809300
LDT=0000 00000000 0000ffff 00008200
TR =0000 00000000 0000ffff 00008b00
GDT=     00000000 00000000
IDT=     00000000 000003ff
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000 
DR6=ffff0ff0 DR7=00000400
CCS=00000000 CCD=00000000 CCO=EFLAGS  
EFER=0000000000000000

For a while, then

check_exception old: 0xffffffff new 0x6

EDIT 2: -d cpu-reset is more helpful then -d int. It prints:

CPU Reset (CPU 0)
EAX=00000032 EBX=00000000 ECX=5153092a EDX=51530000
ESI=0000000a EDI=00100000 EBP=00000000 ESP=00000fb0

(snip)

XMM06=00000000000000000000000000000000 
XMM07=00000000000000000000000000000000
Triple fault

EDIT 3: Full output of -d int -no-reboot -no-shutdown os-image(excluding the SMM exceptions is as follows):

     0: v=20 e=0000 i=0 cpl=0 IP=0008:00001712 pc=00001712 SP=0010:00001f6c env->regs[R_EAX]=000000fa
EAX=000000fa EBX=00007d6a ECX=0000050c EDX=00000060
ESI=00007ca8 EDI=00000000 EBP=00001f80 ESP=00001f6c
EIP=00001712 EFL=00000206 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT=     00007c6b 00000018
IDT=     00000000 00000000
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000
DR6=ffff0ff0 DR7=00000400
CCS=00000004 CCD=00001f50 CCO=EFLAGS  
EFER=0000000000000000
check_exception old: 0xffffffff new 0xd
     1: v=0d e=0102 i=0 cpl=0 IP=0008:00001712 pc=00001712 SP=0010:00001f6c env->regs[R_EAX]=000000fa
EAX=000000fa EBX=00007d6a ECX=0000050c EDX=00000060
ESI=00007ca8 EDI=00000000 EBP=00001f80 ESP=00001f6c
EIP=00001712 EFL=00000206 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT=     00007c6b 00000018
IDT=     00000000 00000000
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000
DR6=ffff0ff0 DR7=00000400
CCS=00000004 CCD=00001f50 CCO=EFLAGS  
EFER=0000000000000000
check_exception old: 0xd new 0xd
     2: v=08 e=0000 i=0 cpl=0 IP=0008:00001712 pc=00001712 SP=0010:00001f6c env->regs[R_EAX]=000000fa
EAX=000000fa EBX=00007d6a ECX=0000050c EDX=00000060
ESI=00007ca8 EDI=00000000 EBP=00001f80 ESP=00001f6c
EIP=00001712 EFL=00000206 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT=     00007c6b 00000018
IDT=     00000000 00000000
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000
DR6=ffff0ff0 DR7=00000400
CCS=00000004 CCD=00001f50 CCO=EFLAGS  
EFER=0000000000000000
check_exception old: 0x8 new 0xd

The complete output including SMM exceptions/interrupts can found on pastebin.

EDIT 4: Here is my interrupt handler (based on osdev wiki's):

void* irq0handler(void) {
    volatile void* address;
    asm goto("jmp %l[ISREnd]" ::: "memory" : ISREnd);                                                                           
// jump to end of ISR
    asm volatile(".align 16" ::: "memory");
    ISRStart:
    asm volatile(
        "pushal             \n\t"                                                                                                                               
// save regs
        "pushl %%ebp        \n\t"
        "movl %%esp, %%ebp  \n\t"
        "cld                "                                                                                                                   
// direction flag is used by gcc
        ::: "memory");
    asm volatile(
        "pushl %%es         \n\t"                                                                                                               
// save segment regs
        "pushl %%ds         \n\t"
        "movw $16, %%cx     \n\t"                                                                                                               
// set segment regs for kernel
        "movw %%cx, %%ds    \n\t"
        "movw %%cx, %%es    \n\t"
        "addl $4, (%%esp)   \n\t"                                                                                                               
// add 4 to make it point to PUSHAD struct
        "call *%%eax        \n\t"                                                                                                               
// call IRQ func.
        :: "a"(irq0func): "memory");                                                                                                        
// eax is the IRQ 0 function
    asm volatile(
        "popl %%es          \n\t"                                                                                                               
// restore everything
        "popl %%ds          \n\t"
        "leave              \n\t"
        "popal              \n\t"
        "iret               "                                                                                                               
// return from interrupt handler
        ::: "memory");
    ISREnd:
    asm goto(
        ".intel_syntax noprefix        \n\t"
        "mov eax, offset %l[ISRStart]  \n\t"                                                                                        
// get address of ISR start
        "mov [ebx], eax                \n\t"                                                                                        
// set ebx to adderss
        ".att_syntax                       "
        :: "b"(&address) : "eax", "memory" : ISRStart);                                                                 
// ebx to be set to adderss
    return (void*) address;                                                                                                                         
// return adderss of ISR
}

void irq0func() {
    // do something
    _PIC_sendEOI(0);
}

It uses the function _PIC_sendEOI(unsigned char irqnum) (sends an end-of interrupt). The handler is loaded by running the function fillidt(idt+(0x20*8), 8, irq0handler(), 0xe, 0):

void fillidt(void* idt, int select, int offset, int type, int perm) {
  size_t idt_ent_size = (size_t) 8;       // 8 bytes per entry

    uint8_t type_attr;

    int segment = 0;

    type_attr = 1;                                                  //     present
    type_attr <<= 2;                                                //     2 bits to set
    type_attr |= perm;                                          // perm is 2 bits
    type_attr <<= 1;                                                // 1 bit to set
    type_attr |= segment;                                       // segment is 1 bit
    type_attr <<= 4;                                                // 4 bits to set
    type_attr |= type;                                          // type is 4 bits

  struct {
    uint16_t offset_1;                    // offset bits 0..15
    uint16_t selector;                    // a code segment selector in the GDT or LDT
    uint8_t zero;                         // unused
    uint8_t type_attr;                                      // type and attributes
    uint16_t offset_2;                    // offset bits 16..31
  } __attribute__ ((packed)) idt_desc = {offset & 0xffff, select, 0,     type_attr, (offset & 0xffff0000) >> 16};
  memcpy(&idt_desc, idt, idt_ent_size);     // copy the descriptor
}

EDIT 5: I know this is is a bit too many edits, but I have committed the latest code to the debug branch of my github project at https://github.com/nm111/NMOS/tree/debug.

EDIT 6: The issue appears to be fixed now, but this cannot be verified until later today, as I currently do not have access to a computer.

tl;dr Don't borrow code from Osdev Wiki.

gcc
x86
interrupt
osdev
protected-mode
asked on Stack Overflow May 13, 2017 by nm111 • edited May 15, 2017 by nm111

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0