Interrupt crash from ring 3
Posted: Sun Nov 17, 2013 6:31 am
Hello,
I have some troubles to get interrupt working when I am in userland.
I got a triple fault from both bochs and qemu. Bochs being more verbose than qemu it tells me that :
Here is some of my code
The GDT part :
The IDT:
The asm handler code for interrupt:
The LTR :
The jump_pm function :
The is no set of ss in the jump_pm since it is set just after with the above code.
And I did change TSS in protected mode, to be sure, with this code :
Finally, here is the piece of code that switch to a task located in 0x30000 (that task only perform an infinite loop).
Interrupts are working well in pure kernel land, without tasks in ring 3.
I don't understand why bochs is telling me SS is null since 0x18 is the valid segment for the kernel stack and 0x20000 is the actual address of the kernel stack.
To test it I only unmasked keyboard interrupt, the kernel does not crash until a key is pressed. So, I think that ring 3 switch works.
Thanks
I have some troubles to get interrupt working when I am in userland.
I got a triple fault from both bochs and qemu. Bochs being more verbose than qemu it tells me that :
I can't figure out why it tells me that since my TSS seems good.interrupt(): SS selector null
interrupt(): SS selector null
interrupt(): SS selector null
Here is some of my code
The GDT part :
Code: Select all
# define GDT_SIZE 8
# define GDT_NULL_ENTRY 0x0
# define GDT_KERNEL_CODE_ENTRY 0x1
# define GDT_KERNEL_DATA_ENTRY 0x2
# define GDT_KERNEL_STACK_ENTRY 0x3
# define GDT_USER_CODE_ENTRY 0x4
# define GDT_USER_DATA_ENTRY 0x5
# define GDT_USER_STACK_ENTRY 0x6
# define GDT_TSS_ENTRY 0x7
struct tss os_tss;
struct gdt_ptr os_gdt;
struct gdt_entry os_gdt_entries[GDT_SIZE];
static void add_gdt_entry(uint8_t entry_num,
uint32_t base,
uint32_t limit,
uint8_t access,
uint8_t flags)
{
struct gdt_entry *entry = &os_gdt_entries[entry_num];
entry->limit1 = limit & 0xFFFF;
entry->limit2 = (limit >> 16) & 0xF;
entry->base1 = base & 0xFFFFF;
entry->base2 = (base >> 24) & 0xFF;
entry->flags = flags & 0xF;
entry->access = access;
}
void setup_gdt(void)
{
os_tss.debug_flag = 0x00;
os_tss.io_map = 0x00;
os_tss.esp0 = 0x20000;
os_tss.ss0 = 0x18;
add_gdt_entry(GDT_NULL_ENTRY, 0, 0, 0, 0);
add_gdt_entry(GDT_KERNEL_CODE_ENTRY, 0, 0xFFFFF, 0x9B, 0xD);
add_gdt_entry(GDT_KERNEL_DATA_ENTRY, 0, 0xFFFFF, 0x93, 0xD);
add_gdt_entry(GDT_KERNEL_STACK_ENTRY, 0x0, 0x0, 0x97, 0x0D);
add_gdt_entry(GDT_USER_CODE_ENTRY, 0x30000, 0x1, 0xFF, 0x0D);
add_gdt_entry(GDT_USER_DATA_ENTRY, 0x30000, 0x1, 0xF3, 0x0D);
add_gdt_entry(GDT_USER_STACK_ENTRY, 0x0, 0x0, 0xF7, 0x0D);
add_gdt_entry(GDT_TSS_ENTRY, (uint32_t) &os_tss, 0x67, 0xE9, 0x00);
os_gdt.size = sizeof (os_gdt_entries) - 1;
os_gdt.ptr = (uint32_t) os_gdt_entries;
__asm__ __volatile__ ("lgdtl %0" : : "m" (os_gdt));
}
Code: Select all
# define IDT_SIZE 255
# define INTERRUPT_GATE 0x8E00
# define TRAP_GATE 0xEF00
struct idt_ptr os_idt;
struct idt_entry os_entry_idt[IDT_SIZE];
static void add_idt_entry(uint8_t num, uint32_t offset, uint16_t type)
{
struct idt_entry *entry = &os_entry_idt[num];
entry->offset_low = offset & 0xFFFF;
entry->offset_high = (offset >> 16) & 0xFFFF;
entry->select = 0x8;
entry->type = type;
}
void setup_idt(void)
{
for (uint8_t i = 0; i < IDT_SIZE; ++i)
add_idt_entry(i, (uint32_t)asm_irq_default, INTERRUPT_GATE);
add_idt_entry(0x21, (uint32_t)asm_irq_keyboard, INTERRUPT_GATE);
add_idt_entry(0x80, (uint32_t)asm_irq_syscall, TRAP_GATE);
os_idt.size = sizeof (os_entry_idt) - 1;
os_idt.ptr = (uint32_t)os_entry_idt;
__asm__ __volatile__ ("lidt %0" : : "m" (os_idt));
}
Code: Select all
.macro SAVE_REGS
pushal
push %ds
push %es
push %fs
push %gs
push %ebx
movw $0x10, %bx
movw %bx, %ds
pop %ebx
.endm
.macro RESTORE_REGS
pop %gs
pop %fs
pop %es
pop %ds
popal
.endm
.macro IRQ_WRAPPER name
.global asm_irq_\name
.global handle_irq_\name
asm_irq_\name:
SAVE_REGS
call handle_irq_\name
mov $0x20, %al
out %al, $0x20
RESTORE_REGS
iret
.endm
IRQ_WRAPPER default
IRQ_WRAPPER keyboard
.global handle_irq_syscall
.global asm_irq_syscall
asm_irq_syscall:
SAVE_REGS
push %eax
call handle_irq_syscall
pop %eax
RESTORE_REGS
iret
Code: Select all
__asm__ __volatile__ ("movw $0x38, %ax \n \
ltr %ax");
k_cons.puts("Jumping to protected mode\n");
jump_pm();
Code: Select all
.global jump_pm
jump_pm:
mov %cr0, %eax
or $1, %ax
mov %eax, %cr0
ljmp $0x08, $pm
pm:
movw $0x10, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
And I did change TSS in protected mode, to be sure, with this code :
Code: Select all
__asm__ __volatile__("movw $0x18, %ax \n\
movw %ax, %ss \n \
movl $0x20000, %esp");
__asm__("movw %%ss, %0 \n \
movl %%esp, %1" : "=m"(os_tss.ss0), "=m"(os_tss.esp0) : );
Code: Select all
cli();
memcpy((char *) 0x30000, &userland_code, 100);
__asm__ __volatile__ ("push $0x33 \n\
push $0x30000 \n\
pushfl \n \
popl %%eax \n \
orl $0x200, %%eax \n \
and $0xffffbfff, %%eax \n \
push %%eax \n \
push $0x23 \n \
push $0x0 \n\
movw $0x2B, %%ax \n \
movw %%ax, %%ds \n \
iret" : : );
I don't understand why bochs is telling me SS is null since 0x18 is the valid segment for the kernel stack and 0x20000 is the actual address of the kernel stack.
To test it I only unmasked keyboard interrupt, the kernel does not crash until a key is pressed. So, I think that ring 3 switch works.
Thanks