Mouse/Keyboard interrupt sometimes not fired?

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
szhou42
Member
Member
Posts: 67
Joined: Thu Apr 28, 2016 12:40 pm
Contact:

Mouse/Keyboard interrupt sometimes not fired?

Post 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?
hannah
Member
Member
Posts: 34
Joined: Wed Oct 12, 2016 11:32 am
Location: At my PC

Re: Mouse/Keyboard interrupt sometimes not fired?

Post by hannah »

My keyboard is based off his Tutorial and it is working fine. Please, send us your code.
szhou42
Member
Member
Posts: 67
Joined: Thu Apr 28, 2016 12:40 pm
Contact:

Re: Mouse/Keyboard interrupt sometimes not fired?

Post 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..
Nable
Member
Member
Posts: 453
Joined: Tue Nov 08, 2011 11:35 am

Re: Mouse/Keyboard interrupt sometimes not fired?

Post 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.
szhou42
Member
Member
Posts: 67
Joined: Thu Apr 28, 2016 12:40 pm
Contact:

Re: Mouse/Keyboard interrupt sometimes not fired?

Post 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.
szhou42
Member
Member
Posts: 67
Joined: Thu Apr 28, 2016 12:40 pm
Contact:

Re: Mouse/Keyboard interrupt sometimes not fired?

Post 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
}
User avatar
Octacone
Member
Member
Posts: 1138
Joined: Fri Aug 07, 2015 6:13 am

Re: Mouse/Keyboard interrupt sometimes not fired?

Post 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);
}
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
szhou42
Member
Member
Posts: 67
Joined: Thu Apr 28, 2016 12:40 pm
Contact:

Re: Mouse/Keyboard interrupt sometimes not fired?

Post 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:
szhou42
Member
Member
Posts: 67
Joined: Thu Apr 28, 2016 12:40 pm
Contact:

Re: Mouse/Keyboard interrupt sometimes not fired?

Post by szhou42 »

...
Last edited by szhou42 on Sat Jan 07, 2017 9:24 pm, edited 1 time in total.
szhou42
Member
Member
Posts: 67
Joined: Thu Apr 28, 2016 12:40 pm
Contact:

Re: Mouse/Keyboard interrupt sometimes not fired?

Post 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.
Post Reply