Page 1 of 1

Mouse/Keyboard interrupt sometimes not fired?

Posted: Fri Jan 06, 2017 8:55 am
by szhou42
I am using the keyboard code in Bran's kernel development tutorial
I find that the keyboard and mouse interrupt only fires most of the time, sometimes i just don't receive any interrupts related to keyboard and mouse.
All my keyboard driver does is registering the keyboard interrupt and wait for interrupts to be fired.
Does anyone else following this tutorial have the same issue?

Re: Mouse/Keyboard interrupt sometimes not fired?

Posted: Fri Jan 06, 2017 4:31 pm
by hannah
My keyboard is based off his Tutorial and it is working fine. Please, send us your code.

Re: Mouse/Keyboard interrupt sometimes not fired?

Posted: Fri Jan 06, 2017 6:50 pm
by szhou42
Hi, here's my keyboard and interrupt code, which is almost identical to the tutorial.

Code: Select all

void keyboard_init() {
    register_interrupt_handler(IRQ_BASE + 1, keyboard_handler);
}

Code: Select all

isr_t interrupt_handlers[256];
void register_interrupt_handler(int num, isr_t handler) {
    if(num < 256)
        interrupt_handlers[num] = handler;
}
void final_irq_handler(register_t reg) {
    if(interrupt_handlers[reg.int_no] != NULL) {
        isr_t handler = interrupt_handlers[reg.int_no];
        handler(&reg);
    }
    irq_ack(reg.int_no);
}
i m trying to check my previous versions and find out which version cause the bug..

Re: Mouse/Keyboard interrupt sometimes not fired?

Posted: Sat Jan 07, 2017 12:14 am
by Nable

Code: Select all

void final_irq_handler(register_t reg) {
It looks like you are using famous buggy tutorial. There is a well-known problem that you should pass registers structure by pointer (not by value) as compiler may make unexpected changes in its arguments that are passed by value.

Re: Mouse/Keyboard interrupt sometimes not fired?

Posted: Sat Jan 07, 2017 3:35 am
by szhou42
Nable wrote:

Code: Select all

void final_irq_handler(register_t reg) {
It looks like you are using famous buggy tutorial. There is a well-known problem that you should pass registers structure by pointer (not by value) as compiler may make unexpected changes in its arguments that are passed by value.
I just tried using pass structure by pointer, still not working.
However, my code always work in qemu without enabling kvm.
It looks like the problem is related to irq acknowledge, because in qemu with kvm enabled, sometimes all my other interrupts would stop firing after a few irq firings.

Re: Mouse/Keyboard interrupt sometimes not fired?

Posted: Sat Jan 07, 2017 3:38 am
by szhou42
szhou42 wrote:
Nable wrote:

Code: Select all

void final_irq_handler(register_t reg) {
It looks like you are using famous buggy tutorial. There is a well-known problem that you should pass registers structure by pointer (not by value) as compiler may make unexpected changes in its arguments that are passed by value.
I just tried using pass structure by pointer, still not working.
However, my code always work in qemu without enabling kvm.
It looks like the problem is related to irq acknowledge, because in qemu with kvm enabled, sometimes all my other interrupts would stop firing after a few irq firings.

Code: Select all

void irq_ack(uint8_t irq) {
    if(irq >= 0x28)
        outportb(PIC2, PIC_EOI); // output 0x20 to 0xA0
    outportb(PIC1, PIC_EOI); // output 0x20 to 0x20
}

Re: Mouse/Keyboard interrupt sometimes not fired?

Posted: Sat Jan 07, 2017 4:27 am
by Octacone
szhou42 wrote:
szhou42 wrote:
Nable wrote:

Code: Select all

void final_irq_handler(register_t reg) {
It looks like you are using famous buggy tutorial. There is a well-known problem that you should pass registers structure by pointer (not by value) as compiler may make unexpected changes in its arguments that are passed by value.
I just tried using pass structure by pointer, still not working.
However, my code always work in qemu without enabling kvm.
It looks like the problem is related to irq acknowledge, because in qemu with kvm enabled, sometimes all my other interrupts would stop firing after a few irq firings.

Code: Select all

void irq_ack(uint8_t irq) {
    if(irq >= 0x28)
        outportb(PIC2, PIC_EOI); // output 0x20 to 0xA0
    outportb(PIC1, PIC_EOI); // output 0x20 to 0x20
}
For the reference, this is how my code looks like. It works just fine. Also 0x28 is 40 in decimal.

Code: Select all

void IRQ_Send_EOI(uint8_t irq_number)
{
	if(irq_number >= 8)
	{
		Outportb(0xA0, 0x20);
	}
	Outportb(0x20, 0x20);
}

Re: Mouse/Keyboard interrupt sometimes not fired?

Posted: Sat Jan 07, 2017 7:22 pm
by szhou42
Sorry i just find out that it's not related to irq ack.
Instead, all interrupts stopped immediately after i enter real mode to set vesa mode and return back to protected mode.

Code: Select all

[bits 32]

global bios32_helper
global bios32_helper_end

global asm_gdt_ptr
global asm_gdt_entries
global asm_idt_ptr
global asm_in_reg_ptr
global asm_out_reg_ptr
global asm_intnum_ptr


extern gdt_init
extern idt_init
extern new_gdt_entries;
extern new_gdt_ptr;
extern new_idt_ptr;
extern new_reg_ptr;
extern new_intnum_ptr;

%define REBASE(x)                              (((x) - bios32_helper) + 0x7c00)
%define GDTENTRY(x)                            ((x) << 3)
%define CODE32                                 GDTENTRY(1)  ; 0x08
%define DATA32                                 GDTENTRY(2)  ; 0x10
%define CODE16                                 GDTENTRY(6)  ; 0x30
%define DATA16                                 GDTENTRY(7)  ; 0x38


PG_BIT_OFF equ 0x7fffffff
PG_BIT_ON equ 0x80000000

section .text
bios32_helper: use32
    pusha
    mov edx, esp
    ; Now in 32bit protected mode
    ; Disable interrupts
    cli
    ; Turn off paging
    mov ecx, cr0
    and ecx, PG_BIT_OFF
    mov cr0, ecx
    ; Zero cr3(save it in ebx before zeroing it)
    xor ecx, ecx
    mov ebx, cr3
    mov cr3, ecx

    ; Load new gdt
    lgdt [REBASE(asm_gdt_ptr)]

    ; Load idt
    lidt [REBASE(asm_idt_ptr)]
   
    jmp CODE16:REBASE(protected_mode_16)
protected_mode_16:use16
    ; Now in 16bit protected mode
    ; Update data segment selector
    mov ax, DATA16
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax

    ; Turn off protected mode
    mov eax, cr0
    and  al,  ~0x01
    mov cr0, eax

    jmp 0x0:REBASE(real_mode_16)
real_mode_16:use16
    ; 16 bit real mode data segment
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    mov sp, 0x8c00

    sti

    ; ### Save current context ###
    pusha
    mov cx, ss
    push cx
    mov cx, gs
    push cx
    mov cx, fs
    push cx
    mov cx, es
    push cx
    mov cx, ds
    push cx
    pushf

    mov ax, sp
    mov edi, temp_esp
    stosw

    ; ### Load the given context from asm_in_reg_ptr ###
    ; Temporaril change esp to asm_in_reg_ptr
    mov esp, REBASE(asm_in_reg_ptr)

    ; only use some general register from the given context
    popa

    ; set a new stack for bios interrupt
    mov sp, 0x9c00
    ; opcode for int
    db 0xCD
asm_intnum_ptr:
    ; put the actual interrupt number here
    db 0x00
    ; ### Write current context to asm_out_reg_ptr ###
    mov esp, REBASE(asm_out_reg_ptr)
    add sp, 28

    pushf
    mov cx, ss
    push cx
    mov cx, gs
    push cx
    mov cx, fs
    push cx
    mov cx, es
    push cx
    mov cx, ds
    push cx
    pusha

    ; ### Restore current context ###
    mov esi, temp_esp
    lodsw
    mov sp, ax

    popf
    pop cx
    mov ds, cx
    pop cx
    mov es, cx
    pop cx
    mov fs, cx
    pop cx
    mov gs, cx
    pop cx
    mov ss, cx
    popa

    mov eax, cr0
    inc eax
    mov cr0, eax
    jmp CODE32:REBASE(protected_mode_32)
protected_mode_32:use32
    mov ax, DATA32
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax

    ; restore cr3
    mov cr3, ebx

    ; Turn on paging
    mov ecx, cr0
    or ecx, PG_BIT_ON
    mov cr0, ecx
    
    ; restore esp
    mov esp, edx

    sti
    popa
    ret
padding:
    db 0x0
    db 0x0
    db 0x0
asm_gdt_entries:
    ; 8 gdt entries
    resb 64
asm_gdt_ptr:
    dd 0x00000000
    dd 0x00000000
asm_idt_ptr:
    dd 0x00000000
    dd 0x00000000
asm_in_reg_ptr:
    resw 14
asm_out_reg_ptr:
    dd 0xaaaaaaaa
    dd 0xaaaaaaaa
    dd 0xaaaaaaaa
    dd 0xaaaaaaaa
    dd 0xaaaaaaaa
    dd 0xaaaaaaaa
    dd 0xaaaaaaaa
temp_esp:
    dw 0x0000
bios32_helper_end:

Re: Mouse/Keyboard interrupt sometimes not fired?

Posted: Sat Jan 07, 2017 9:06 pm
by szhou42
...

Re: Mouse/Keyboard interrupt sometimes not fired?

Posted: Sat Jan 07, 2017 9:24 pm
by szhou42
This is super weird....
All I did is adding a line of code to to manually trigger an interrupt and see if interrupt works, interrupts start firing..

Code: Select all

asm volatile("int $0x20");
I think the reason is when I am returning to real mode to call bios, some interrupts is not being acknowledged so no more interrupts can be fired.
By manually triggering an interrupt, and acknowledging it, the PIC would start firing interrupts again.

So, directly acknowledging an irq would do the trick as well as manually trigger an interrupt. I tried it and it also works.