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.
User contributions licensed under CC BY-SA 3.0