Notice anything wrong with my code?

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
Exabyte256

Notice anything wrong with my code?

Post by Exabyte256 »

I've got further with the system; I've remapped the PIC, created an IDT, and can read from the keyboard.

This works fine in bochs, and it's disappointing for my computer (Toshiba Satellite Pro) to instantly reset. After much debugging, CD-Writing and restarting, I worked out that it was when I called a high-numbered interrupt, that it crashed. I can call 32, 33, 34... then I tried the one used in unix systems (0x80) and it works in bochs, but not on my computer.

So I'm completely stuck. Help!

Code: Select all

void CalculateGate(unsigned char* descriptor, void* function)
{
   // Get the address.
   unsigned int address = (unsigned int)function;
   unsigned char* addressSplit = (unsigned char*)&address;

   // Set the stuff.
   descriptor[0] = addressSplit[0];
   descriptor[1] = addressSplit[1];
   descriptor[6] = addressSplit[2];
   descriptor[7] = addressSplit[3];

   // Enable the interrupt.
   descriptor[5] |= 0x80;
}

void SetupGates(void* function, unsigned short position, unsigned short count, void* idt)
{
   unsigned short t;
   idt += position * 8;
   for(t = 0; t < count; t++)
   {
      CalculateGate(idt, function);
      idt += 8;
   }
}
(Remember. All this code works fine in bochs, and for lower numbered interrupts on the hardware.)

Code: Select all

; Note that irrelevant code has been cut
push IDTStart
mov eax, 256
push eax
mov eax, 0
push eax
push _itest
call _SetupGates
add esp, 16

lidt[IDTR]

int 32         ; Works.
int 33         ; Works.
int 34         ; Works.
int 0x80      ; Works(bochs). Resets the computer(hardware).

jmp $

_itest:
 mov eax, mFoo
 push eax
 call _Print
 add esp, 0x04
 iret
mFoo db "Interrupt!", 0x00

IDTR:
 dw IDTEnd - IDTStart
 dd IDTStart

; This should describe an unprotected flat memory model:
align 8, db 0      ; Optimization!
IDTStart:
 times 0x14 dd 0x00000000, 0x00000000      ; Exceptions!
 times 0x0C   dd 0x00000000, 0x00000000      ; Intel's reserved space.
 times 0xD0   dd 0x00080000, 0x00000E00      ; Interrupts for me, interrupts for you.
 times 0x10 dd 0x00080000, 0x00000E00      ; Interrupts for the IRQ lines.
IDTEnd:

User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:Notice anything wrong with my code?

Post by Pype.Clicker »

from the behaviour you describe, i can envision two possible issues:
- your IDT is not exactly standing where you expect it to be, but the offset is low enough so that e.g. descriptor for INT 30 still maps correctly to some interrupt descriptor
- your IDT limit is not correctly set, which makes an exception to be triggered when you try to call something beyond that limit.

However, your code seems clean on both aspects ... Do you have hardware interrupts disabled while you're programming your IDT ? since the handler doesn't preserve registers, you might not like to have it called in the middle of something else ...
Exabyte256

Re:Notice anything wrong with my code?

Post by Exabyte256 »

Yes I have hardware interrupts disabled all the way through now this problem has arisen. I had my IRQ remapped to 0xF0-0xFF since I happened to like that layout.


Hrm. I wonder...

Maybe I should try mapping it to 0x20 like everyone else. The hardware designers may have expected it to be mapped to a low point and created the PIC around that idea - there is a "made for windows XP" sticker on this machine after all.

Edit: Nope. Didn't work. So I'll set it back at 0xF0.
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:Notice anything wrong with my code?

Post by Pype.Clicker »

could it simply be that 'print' didn't like the 0x80 ?

I know that sounds silly, but if you put 0x80 into a char and then convert it into an int, you'll get something like 0xFFFFFF80 (sign extension).

If you later try to shift that, you'll get 0xFFFFFFF8 (sign conservation when shifting signed numbers). So if you're waiting for that number to reach zero before you end some recursion or loop, the system won't be pleased.

Can you be more specific at "it crashed" ? does it reboots from itself ? does it hang ? does it do weird beeps or display weird things ? does it drive straight into a tree ? ... err ... no, forget that last one.
Exabyte256

Re:Notice anything wrong with my code?

Post by Exabyte256 »

It simply resets. So probably the equivilent of that bochs exception-with-no-resolution-and-I'm-not-going-to-tell-you-anything-at-all.

Edit:
I removed the _print instructions and it did the same triplefault-like reset.
Exabyte256

Re:Notice anything wrong with my code?

Post by Exabyte256 »

I fixed the problem!

I decided to go and look at the Linux source to see how those who knew how did it. I only saw their base "fill-in-the-table" code, but they had the same idea by setting up all 256 entries. The way they did it was different though, they used C code, with some sort of assembly macros.

Code: Select all

 setup_idt() {
                        /* idt_table[256] defined in arch/i386/kernel/traps.c
                         *   located in section .data.idt
                        EAX = __KERNEL_CS << 16 + ignore_int;
                        DX = 0x8E00;    // interrupt gate, dpl = 0, present
                        idt_table[0..255] = {EAX, EDX};
                }
The code isn't really what I'd call 'elegant', but I know it works on my machine.

So, last resort, I deleted all my C functions and made an assembly function to fill in the table instead. I got it working in bochs, then I tried it on actual hardware and it works perfectly. From 0x20 to 0x80 to 0xFF.

Code: Select all

 ; Map the interrupts to a function.
 mov eax, 0x00080000   ; Interrupt part 1 to eax.
 mov edx, _itest      ; The function to map.
 and edx, 0x0000FFFF   ; Get the lower half of the function's address.
 or eax, edx         ; Add that to eax.
 
 mov ebx, 0x00008E00   ; Interrupt part 2 to ebx. (Including the type and flags)
 mov edx, _itest      ; The function to map again.
 and edx, 0xFFFF0000   ; Get the higher half.
 or ebx, edx         ; Add it to ebx. Note there's no bitshifting needed.
 
 mov ecx, 256         ; Fill 256 entries.
 mov edx, IDTStart      ; The start of the table.
 
 FillInterrupts:
 mov [ds:edx], eax      ; Set interrupt part 1.
 add edx, 4            ; Move on to the next part.
 mov [ds:edx], ebx      ; Set interrupt part 2.
 add edx, 4            ; Move on to the next entry.
 loop FillInterrupts   ; Loop until all the interrupts have been filled.
I think the moral to this solution is something like: Don't let a GCC do an assembler's job.
That, or don't trust Exabyte with a C compiler. :p
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:Notice anything wrong with my code?

Post by Pype.Clicker »

the main difference between a emulator and a real machine is that an emulator always present you a page full of zeroes when you access it first. That means that if - for some reasons - you didn't initialized something, you'll not discover your error.

This may become very nasty if -for instance for the sake of initializing datas- GCC uses some structure stored on .bss ... on a 'normal' environment, the loader takes care of having .bss full of zeroes, but unless you *enforce* this in your bootloader, this won't be the case for your kernel.
Post Reply