I think my problem is a silly error on my part but I have been scouting the OSDev wiki and other resources for a while and am still lost.
I am making a small OS in x86_64 assembly and in C and I am trying to add keyboard support to build a shell. Everything boots in QEMU using multiboot in long mode until I add the sti
command in my IDT loader, where my OS proceeds to triple fault (I'm pretty sure) on boot. I know other people have had similar problems, but I haven't found a solution to mine.
Below is the 64-bit assembly file:
global keyboard_handler
global read_port
global write_port
global load_idt
global long_mode_start
extern kernel_main
extern keyboard_handler_main
section .text
bits 64
read_port:
mov edx, [esp + 4]
in al, dx ; al is the lower 8 bits of eax; dx is the lower 16 bits of edx
ret
write_port:
mov edx, [esp + 4]
mov al, [esp + 8]
out dx, al
ret
load_idt:
mov edx, [esp + 6]
lidt [edx]
;sti ; turn on interrupts
ret
keyboard_handler:
call keyboard_handler_main
iretd
long_mode_start:
; Load NULL into all data registers
mov ax, 0
mov ss, ax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
call kernel_main
hlt
And here is an excerpt in the fault trail (using -d int in QEMU) after I uncomment sti
in load_idt
:
check_exception old: 0xffffffff new 0xd
0: v=0d e=0800 i=0 cpl=0 IP=0008:0000000000100c7d pc=0000000000100c7d SP=0000:0000000000108fb8 env->regs[R_EAX]=0000000000108fc0
RAX=0000000000108fc0 RBX=0000000000000000 RCX=0000000000000036 RDX=0000000000000000
RSI=000000000000000a RDI=0000000000108fc0 RBP=0000000000108fe0 RSP=0000000000108fb8
R8 =0000000000000000 R9 =0000000000000000 R10=0000000000000000 R11=0000000000000000
R12=0000000000000000 R13=0000000000000000 R14=0000000000000000 R15=0000000000000000
RIP=0000000000100c7d RFL=00000206 [-----P-] CPL=0 II=1 A20=1 SMM=0 HLT=0
ES =0000 0000000000000000 00000000 00000000
CS =0008 0000000000000000 00000000 00209a00 DPL=0 CS64 [-R-]
SS =0000 0000000000000000 00000000 00000000
DS =0000 0000000000000000 00000000 00000000
FS =0000 0000000000000000 00000000 00000000
GS =0000 0000000000000000 00000000 00000000
LDT=0000 0000000000000000 0000ffff 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT= 00000000001001b8 0000000f
IDT= e2c3f000ff53f000 0000ff53
CR0=80000011 CR2=0000000000000000 CR3=0000000000102000 CR4=00000020
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=0000000000000001 CCD=0000000000000006 CCO=ADDQ
EFER=0000000000000500
check_exception old: 0xd new 0xd
1: v=08 e=0000 i=0 cpl=0 IP=0008:0000000000100c7d pc=0000000000100c7d SP=0000:0000000000108fb8 env->regs[R_EAX]=0000000000108fc0
RAX=0000000000108fc0 RBX=0000000000000000 RCX=0000000000000036 RDX=0000000000000000
RSI=000000000000000a RDI=0000000000108fc0 RBP=0000000000108fe0 RSP=0000000000108fb8
R8 =0000000000000000 R9 =0000000000000000 R10=0000000000000000 R11=0000000000000000
R12=0000000000000000 R13=0000000000000000 R14=0000000000000000 R15=0000000000000000
RIP=0000000000100c7d RFL=00000206 [-----P-] CPL=0 II=1 A20=1 SMM=0 HLT=0
ES =0000 0000000000000000 00000000 00000000
CS =0008 0000000000000000 00000000 00209a00 DPL=0 CS64 [-R-]
SS =0000 0000000000000000 00000000 00000000
DS =0000 0000000000000000 00000000 00000000
FS =0000 0000000000000000 00000000 00000000
GS =0000 0000000000000000 00000000 00000000
LDT=0000 0000000000000000 0000ffff 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT= 00000000001001b8 0000000f
IDT= e2c3f000ff53f000 0000ff53
CR0=80000011 CR2=0000000000000000 CR3=0000000000102000 CR4=00000020
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=0000000000000001 CCD=0000000000000006 CCO=ADDQ
EFER=0000000000000500
check_exception old: 0x8 new 0xd
2: v=03 e=0000 i=1 cpl=0 IP=0008:00000000000f1203 pc=00000000000f1203 SP=0010:0000000000000fb4 env->regs[R_EAX]=00000000000f6006
I have checked my OI handling and ISR descriptor, and I think they're fine. I think I am just doing something silly with my memory movement in load_idt
, but any advice would be much appreciated. I'll be happy to provide more code / explanation. Thank you.
EDIT: Here is where load_idt
is called (variables in CAPS are all the usual values). I have given the 32-bit and 64-bit (commented) initialization of the IDT. Someone made a good point that my read/write ports may not be working properly, which could explain things:
#include "keyboard.h"
#include "print.h"
unsigned char keyboard_map[128] =
{
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', /* 9 */
'9', '0', '-', '=', '\b', /* Backspace */
'\t', /* Tab */
'q', 'w', 'e', 'r', /* 19 */
't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', /* Enter key */
0, /* 29 - Control */
'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', /* 39 */
'\'', '`', 0, /* Left shift */
'\\', 'z', 'x', 'c', 'v', 'b', 'n', /* 49 */
'm', ',', '.', '/', 0, /* Right shift */
'*',
0, /* Alt */
' ', /* Space bar */
0, /* Caps lock */
0, /* 59 - F1 key ... > */
0, 0, 0, 0, 0, 0, 0, 0,
0, /* < ... F10 */
0, /* 69 - Num lock*/
0, /* Scroll Lock */
0, /* Home key */
0, /* Up Arrow */
0, /* Page Up */
'-',
0, /* Left Arrow */
0,
0, /* Right Arrow */
'+',
0, /* 79 - End key*/
0, /* Down Arrow */
0, /* Page Down */
0, /* Insert Key */
0, /* Delete Key */
0, 0, 0,
0, /* F11 Key */
0, /* F12 Key */
0, /* All other keys are undefined */
};
/* current cursor location */
unsigned int current_loc = 0;
/* video memory begins at address 0xb8000 */
char *vidptr = (char*)0xb8000;
struct IDT_entry {
unsigned short int offset_lowerbits;
unsigned short int selector;
unsigned char zero;
unsigned char type_attr;
unsigned short int offset_higherbits;
};
struct IDT_entry_64 { // Will possibly use later
unsigned short int offset_lowerbits;
unsigned short int selector;
unsigned char ist;
unsigned char type_attr;
unsigned short int offset_middlebits;
unsigned long int offset_higherbits;
unsigned long int zero;
};
struct IDT_entry IDT[IDT_SIZE];
void idt_init(void) {
unsigned long keyboard_address;
unsigned long idt_address;
unsigned long idt_ptr[2];
/* populate IDT entry of keyboard's interrupt */
keyboard_address = (unsigned long)keyboard_handler;
print_str( " Keyboard address: " );
print_int( keyboard_address );
print_newline();
IDT[0x21].offset_lowerbits = keyboard_address & 0x0000ffff;
IDT[0x21].selector = KERNEL_CODE_SEGMENT_OFFSET;
IDT[0x21].zero = 0;
IDT[0x21].type_attr = INTERRUPT_GATE;
IDT[0x21].offset_higherbits = (keyboard_address & 0xffff0000) >> 16;
// IDT[0x21].offset_lowerbits = keyboard_address & 0x000000000000ffff;
// IDT[0x21].offset_middlebits = (keyboard_address & 0x00000000ffff0000) >> 16;
// IDT[0x21].offset_higherbits = (keyboard_address & 0xffffffff00000000) >> 32;
// IDT[0x21].selector = KERNEL_CODE_SEGMENT_OFFSET;
// IDT[0x21].ist = 0;
// IDT[0x21].type_attr = INTERRUPT_GATE;
// IDT[0x21].zero = 0;
/* Ports
* PIC1 PIC2
*Command 0x20 0xA0
*Data 0x21 0xA1
*/
/* ICW1 - begin initialization */
write_port(0x20 , 0x11);
write_port(0xA0 , 0x11);
/* ICW2 - remap offset address of IDT */
/*
* In x86 protected mode, we have to remap the PICs beyond 0x20 because
* Intel have designated the first 32 interrupts as "reserved" for cpu exceptions
*/
write_port(0x21 , 0x20);
write_port(0xA1 , 0x28);
/* ICW3 - setup cascading */
write_port(0x21 , 0x00);
write_port(0xA1 , 0x00);
/* ICW4 - environment info */
write_port(0x21 , 0x01);
write_port(0xA1 , 0x01);
/* Initialization finished */
/* mask interrupts */
write_port(0x21 , 0xfd);
write_port(0xA1 , 0xff);
/* fill the IDT descriptor */
idt_address = (unsigned long)IDT;
print_str( " IDT address: " );
print_int( idt_address );
print_newline();
print_str( " Pre-assignment IDT pointers: " );
print_int( idt_ptr[0] );
print_str( ", " );
print_int( idt_ptr[1] );
print_newline();
idt_ptr[0] = (sizeof (struct IDT_entry) * IDT_SIZE) + ((idt_address & 0x00000000ffffffff) << 32);
idt_ptr[1] = idt_address >> 16;
print_str( " Post-assignment IDT pointers: " );
print_int( idt_ptr[0] );
print_str( ", " );
print_int( idt_ptr[1] );
print_newline();
load_idt(idt_ptr);
}
void keyboard_init(void) {
/* 0xFD is 11111101 - enables only IRQ1 (keyboard)*/
write_port(0x21 , 0xFD);
}
void keyboard_handler_main(void) {
unsigned char status;
char keycode;
/* write EOI */
write_port(0x20, 0x20);
status = read_port(KEYBOARD_STATUS_PORT);
/* Lowest bit of status will be set if buffer is not empty */
if( status & 0x01 ) {
keycode = read_port(KEYBOARD_DATA_PORT);
if(keycode < 0)
return;
if( keycode == ENTER_KEY_CODE ) {
print_str( " Pressed enter" );
print_newline();
return;
}
vidptr[current_loc++] = keyboard_map[(unsigned char) keycode];
vidptr[current_loc++] = 0x07;
}
}