Cannot get keyboard interrupt handler working

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.
MichaelPetch
Member
Member
Posts: 798
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: Cannot get keyboard interrupt handler working

Post by MichaelPetch »

You likely didn't take seriously the earlier advice in this thread about creating your own GDT. This wasn't just a good idea, it is pretty much a necessity especially if you intend to boot with a real version of GRUB. I have written about this issue on Stackoverflow. The multiboot specification says clearly that you can't rely on the GDT record being valid and that you need to create your own if you intend to reload any of the segment register. This will occur if you use interrupts.

A quick and dirty hack would be to create one in boot.S.Modify that file to be:

Code: Select all

# Declare constants for the multiboot header.
.set ALIGN,    1<<0             # align loaded modules on page boundaries
.set MEMINFO,  1<<1             # provide memory map
.set FLAGS,    ALIGN | MEMINFO  # this is the Multiboot 'flag' field
.set MAGIC,    0x1BADB002       # 'magic number' lets bootloader find the header
.set CHECKSUM, -(MAGIC + FLAGS) # checksum of above, to prove we are multiboot

# Declare a header as in the Multiboot Standard.
.section .multiboot
.align 4
.long MAGIC
.long FLAGS
.long CHECKSUM

# Reserve a stack for the initial thread.
.section .bss
.align 16
stack_bottom:
.skip 16384 # 16 KiB
stack_top:

# The kernel entry point.
.section .text
.global _start
.type _start, @function
_start:
        movl $stack_top, %esp

        # Call the global constructors.
        call _init

    # Load our own GDT as the multioot GDT record may be invalid
    call load_gdt

        # Transfer control to the main kernel.
        call kernel_main

        # Hang if kernel_main unexpectedly returns.
        cli
1:      # jmp $
        hlt
        jmp 1b
.size _start, . - _start

# Load GDT and set selectors for a flat memory model
load_gdt:
    lgdt (gdtr)
    ljmp $CODE_SEL, $.setcs             # Set CS selector with far JMP
.setcs:
    mov $DATA_SEL, %eax                 # Set the Data selectors to defaults
    mov %eax, %ds
    mov %eax, %es
    mov %eax, %ss
    mov %eax, %fs
    mov %eax, %gs
    ret

.global load_gdt
.section .data

# GDT with a NULL Descriptor, a 32-Bit code Descriptor
# and a 32-bit Data Descriptor
gdt_start:
gdt_null:
    .long 0x0
    .long 0x0

gdt_code:
    .short 0xffff
    .short 0x0
    .byte 0x0
    .byte 0b10011010
    .byte 0b11001111
    .byte 0x0

gdt_data:
    .short 0xffff
    .short 0x0
    .byte 0x0
    .byte 0b10010010
    .byte 0b11001111
    .byte 0x0
gdt_end:

# GDT descriptor record
gdtr:
    .short gdt_end - gdt_start - 1
    .long gdt_start

CODE_SEL = gdt_code - gdt_start
DATA_SEL = gdt_data - gdt_start
In your idt_init function you have:

Code: Select all

idt_ptr[0] = (sizeof (struct IDT_entry) * 286) + ((idt_address & 0xffff) << 16);
. It should be 256, not 286. Near the bottom of the same function you should set the IDT size with something like:

Code: Select all

idt_ptr[0] = (sizeof (IDT)-1) + ((idt_address & 0xffff) << 16);
. You originally used 286 as well. The IDT structure is the entire size. You have to subtract one from the length before storing it in the IDT record (idt_ptr).

In your kernel_main you should add an infinite loop that issues a hlt. At the very end of that function add:

Code: Select all

while(1) asm ("hlt");
xSlendiX
Posts: 20
Joined: Tue Jan 08, 2019 7:20 am

Re: Cannot get keyboard interrupt handler working

Post by xSlendiX »

MichaelPetch wrote:You likely didn't take seriously the earlier advice in this thread about creating your own GDT. This wasn't just a good idea, it is pretty much a necessity especially if you intend to boot with a real version of GRUB. I have written about this issue on Stackoverflow. The multiboot specification says clearly that you can't rely on the GDT record being valid and that you need to create your own if you intend to reload any of the segment register. This will occur if you use interrupts.

A quick and dirty hack would be to create one in boot.S.Modify that file to be:

Code: Select all

# Declare constants for the multiboot header.
.set ALIGN,    1<<0             # align loaded modules on page boundaries
.set MEMINFO,  1<<1             # provide memory map
.set FLAGS,    ALIGN | MEMINFO  # this is the Multiboot 'flag' field
.set MAGIC,    0x1BADB002       # 'magic number' lets bootloader find the header
.set CHECKSUM, -(MAGIC + FLAGS) # checksum of above, to prove we are multiboot

# Declare a header as in the Multiboot Standard.
.section .multiboot
.align 4
.long MAGIC
.long FLAGS
.long CHECKSUM

# Reserve a stack for the initial thread.
.section .bss
.align 16
stack_bottom:
.skip 16384 # 16 KiB
stack_top:

# The kernel entry point.
.section .text
.global _start
.type _start, @function
_start:
        movl $stack_top, %esp

        # Call the global constructors.
        call _init

    # Load our own GDT as the multioot GDT record may be invalid
    call load_gdt

        # Transfer control to the main kernel.
        call kernel_main

        # Hang if kernel_main unexpectedly returns.
        cli
1:      # jmp $
        hlt
        jmp 1b
.size _start, . - _start

# Load GDT and set selectors for a flat memory model
load_gdt:
    lgdt (gdtr)
    ljmp $CODE_SEL, $.setcs             # Set CS selector with far JMP
.setcs:
    mov $DATA_SEL, %eax                 # Set the Data selectors to defaults
    mov %eax, %ds
    mov %eax, %es
    mov %eax, %ss
    mov %eax, %fs
    mov %eax, %gs
    ret

.global load_gdt
.section .data

# GDT with a NULL Descriptor, a 32-Bit code Descriptor
# and a 32-bit Data Descriptor
gdt_start:
gdt_null:
    .long 0x0
    .long 0x0

gdt_code:
    .short 0xffff
    .short 0x0
    .byte 0x0
    .byte 0b10011010izeof (IDT)-1) + ((idt_address & 0xffff) << 16);
. You origin
    .byte 0b11001111
    .byte 0x0

gdt_data:
    .short 0xffff
    .short 0x0
    .byte 0x0
    .byte 0b10010010
    .byte 0b11001111
    .byte 0x0izeof (IDT)-1) + ((idt_address & 0xffff) << 16);
. You origin
gdt_end:

# GDT descriptor record
gdtr:
    .short gdt_end - gdt_start - 1
    .long gdt_start

CODE_SEL = gdt_code - gdt_start
DATA_SEL = gdt_data - gdt_start
In your idt_init function you have:

Code: Select all

idt_ptr[0] = (sizeof (struct IDT_entry) * 286) + ((idt_address & 0xffff) << 16);
. It should be 256, not 286. Near the bottom of the same function you should set the IDT size with something like:

Code: Select all

idt_ptr[0] = (sizeof (IDT)-1) + ((idt_address & 0xffff) << 16);
. You originally used 286 as well. The IDT structure is the entire size. You have to subtract one from the length before storing it in the IDT record (idt_ptr).

In your kernel_main you should add an infinite loop that issues a hlt. At the very end of that function add:

Code: Select all

while(1) asm ("hlt");
Thanks for the reply! I will surely try it when I can!

EDIT: Now I get an error: can't handle non absolute segment in `ljmp'
Hello world!
MichaelPetch
Member
Member
Posts: 798
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: Cannot get keyboard interrupt handler working

Post by MichaelPetch »

In your response there is a bunch of junk in the GDT entries that seem to have come from my comments.You will have to move the `.data` section containing the GDT before the `.text` section to avoid the LJMP issue. The file you might wish to try is this one:

Code: Select all

# Declare constants for the multiboot header.
.set ALIGN,    1<<0             # align loaded modules on page boundaries
.set MEMINFO,  1<<1             # provide memory map
.set FLAGS,    ALIGN | MEMINFO  # this is the Multiboot 'flag' field
.set MAGIC,    0x1BADB002       # 'magic number' lets bootloader find the header
.set CHECKSUM, -(MAGIC + FLAGS) # checksum of above, to prove we are multiboot

# Declare a header as in the Multiboot Standard.
.section .multiboot
.align 4
.long MAGIC
.long FLAGS
.long CHECKSUM

# Reserve a stack for the initial thread.
.section .bss
.align 16
stack_bottom:
.skip 16384 # 16 KiB
stack_top:

.section .data

# GDT with a NULL Descriptor, a 32-Bit code Descriptor
# and a 32-bit Data Descriptor
gdt_start:
gdt_null:
    .long 0x0
    .long 0x0

gdt_code:
    .short 0xffff
    .short 0x0
    .byte 0x0
    .byte 0b10011010
    .byte 0b11001111
    .byte 0x0

gdt_data:
    .short 0xffff
    .short 0x0
    .byte 0x0
    .byte 0b10010010
    .byte 0b11001111
    .byte 0x0
gdt_end:

# GDT descriptor record
gdtr:
    .short gdt_end - gdt_start - 1
    .long gdt_start

CODE_SEL = gdt_code - gdt_start
DATA_SEL = gdt_data - gdt_start

# The kernel entry point.
.section .text
.global _start
.type _start, @function
_start:
        movl $stack_top, %esp

        # Call the global constructors.
        call _init

    # Load our own GDT as the multioot GDT record may be invalid
    call load_gdt

        # Transfer control to the main kernel.
        call kernel_main

        # Hang if kernel_main unexpectedly returns.
        cli
1:      # jmp $
        hlt
        jmp 1b
.size _start, . - _start

.global load_gdt
# Load GDT and set selectors for a flat memory model
load_gdt:
    lgdt (gdtr)
    ljmp $CODE_SEL, $.setcs             # Set CS selector with far JMP
.setcs:
    mov $DATA_SEL, %eax                 # Set the Data selectors to defaults
    mov %eax, %ds
    mov %eax, %es
    mov %eax, %ss
    mov %eax, %fs
    mov %eax, %gs
    ret
xSlendiX
Posts: 20
Joined: Tue Jan 08, 2019 7:20 am

Re: Cannot get keyboard interrupt handler working

Post by xSlendiX »

Nice! It now works! Thank you!
Hello world!
xSlendiX
Posts: 20
Joined: Tue Jan 08, 2019 7:20 am

Re: Cannot get keyboard interrupt handler working

Post by xSlendiX »

Never mind... For some reason, when I press a key it just prints a line like that one
Attachments
2019-03-05-215457_726x429_scrot.png
Hello world!
MichaelPetch
Member
Member
Posts: 798
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: Cannot get keyboard interrupt handler working

Post by MichaelPetch »

You'd have to update your git code. It doesn't behave that way when I run it. That doesn't mean there isn't a problem. If there is one it would likely be your print routine.At this point in time I'd highly recommend starting to use QEMU's remote GDB debugging feature and use GDB to step through the code,set breakpoints etc. This will also mean compiling your code with debug info to be useful.
xSlendiX
Posts: 20
Joined: Tue Jan 08, 2019 7:20 am

Re: Cannot get keyboard interrupt handler working

Post by xSlendiX »

MichaelPetch wrote:You'd have to update your git code. It doesn't behave that way when I run it. That doesn't mean there isn't a problem. If there is one it would likely be your print routine.At this point in time I'd highly recommend starting to use QEMU's remote GDB debugging feature and use GDB to step through the code,set breakpoints etc. This will also mean compiling your code with debug info to be useful.
Pushed the updated code

Also followed you on twitter, you're such a kind person and you helped me with like everything. Thanks.
Hello world!
MichaelPetch
Member
Member
Posts: 798
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: Cannot get keyboard interrupt handler working

Post by MichaelPetch »

Your code in the keyboard handler does this:

Code: Select all

        printf(",");
	printf((char) keyboard_to_ascii(keycode));
It doesn't appear you are using printf correctly.If you want to print out the ASCII character you'd do something like:

Code: Select all

printf(",%c", keyboard_to_ascii(keycode));
That would print out a comma and then the ASCII character.
xSlendiX
Posts: 20
Joined: Tue Jan 08, 2019 7:20 am

Re: Cannot get keyboard interrupt handler working

Post by xSlendiX »

MichaelPetch wrote:Your code in the keyboard handler does this:

Code: Select all

        printf(",");
	printf((char) keyboard_to_ascii(keycode));
It doesn't appear you are using printf correctly.If you want to print out the ASCII character you'd do something like:

Code: Select all

printf(",%c", keyboard_to_ascii(keycode));
That would print out a comma and then the ASCII character.
Wow that was such an easy solution! I feel a little bit dumb right now but thanks! Sometimes, small thing do big things
Hello world!
Post Reply