Page 1 of 1
IDT in C
Posted: Thu Oct 20, 2011 3:31 pm
by xleelz
I've recently decided to code my kernel in C instead of assembly and I ran across a problem when I tried to install the IDT.
Code: Select all
#include "./kernel/idt.h"
#include "./kernel/datastruc.h"
#include "./kernel/io.h"
#include "./kernel/pic.h"
void int50(void);
void kmain()
{
ints(false);
maskIrq(ALL);
void (*int50ptr)(void);
int50ptr = int50;
addisr(0x50,int50ptr,0);
setIDT();
unsigned char *vidmem = (unsigned char *) 0xB8000;
for(int i=0;i<=160*25;i+=2)
{
vidmem[i]=' ';
vidmem[i+1]=0x1f;
}
ints(true);
__asm__ volatile("int $0x50"); //<---The problem is calling it. In the Virtual Box debugger it shows that int 50 is installed, but it freezes when it's called.
vidmem[0] = 'A';
vidmem[1] = 0x1f;
__asm__ volatile ("cli;hlt");
}
void int50(void)
{
__asm__ __volatile__("pusha;push %es;push %ds; push %fs; push %gs");
unsigned char *vidmem = (unsigned char *) 0xB8E00;
vidmem[0] = 'U';
__asm__ __volatile__("pop %gs; pop %fs; pop %ds; pop %es; popa; iret;");
}
I'm not exactly sure what I'm doing wrong, seeing as how I've done most of my kernel in asm before. Any advice?
Re: IDT in C
Posted: Thu Oct 20, 2011 3:47 pm
by Combuster
C functions can't be interrupt entry points. For example:
Code: Select all
__asm__ __volatile__("pusha;push %es;push %ds; push %fs; push %gs");
will be executed when it's already too late. (Besides the fact that it completely messes up your stack.)
You need assembly for those things.
Re: IDT in C
Posted: Thu Oct 20, 2011 4:57 pm
by xleelz
Ok... maybe I'm not so good at C but my solution (had a similar problem but didn't freeze, just ran into random memory):
isrs.asm:
Code: Select all
[extern int50]
[global int50ptr]
int50ptr:
pushad
push es
push ds
call int50
pop ds
pop es
popad
iret
kernel (again):
Code: Select all
#include "./kernel/idt.h"
#include "./kernel/datastruc.h"
#include "./kernel/io.h"
#include "./kernel/pic.h"
extern void int50ptr(void);
void int50(void);
void kmain()
{
ints(false);
maskIrq(ALL);
addisr(0x50,int50ptr,0);
setIDT();
unsigned char *vidmem = (unsigned char *) 0xB8000;
for(int i=0;i<=160*25;i+=2)
{
vidmem[i]=' ';
vidmem[i+1]=0x1f;
}
ints(true);
__asm__ volatile("int $0x50");
vidmem[0] = 'A';
vidmem[1] = 0x1f;
__asm__ volatile ("cli;hlt");
}
void int50(void)
{
unsigned char *vidmem = (unsigned char *) 0xB8E00;
vidmem[0] = 'U';
}
There is something I'm really not getting here. Any other advise?
A detailed explanation would be preferable.
Re: IDT in C
Posted: Thu Oct 20, 2011 5:24 pm
by gerryg400
A detailed explanation would be preferable.
A detailed description of the problem would be required. That will require some debugging on your part.
Is your linker resolving references between the 2 files ?
You call some external functions, have you unit tested each of them ?
Have you printed out your IDT ? Was it correct ?
Does the int vector correctly ?
Does it make it to the assembler stub ?
Does it get to the C code ?
Does it ret ?
Does it iret ?
What is the last good instruction that runs ?
By the time you answer these questions you will likely have found and fixed the bug.
Re: IDT in C
Posted: Thu Oct 20, 2011 8:06 pm
by Jvac
A detailed description of the problem would be required. That will require some debugging on your part.
Agree!
Real mode or protected mode?
See the
Wiki
Interrupt descriptor table
See this
tutorial
Re: IDT in C
Posted: Thu Oct 20, 2011 8:43 pm
by Chandra
xleelz wrote:There is something I'm really not getting here. Any other advise?
Code: Select all
void int50(void)
{
unsigned char *vidmem = (unsigned char *) 0xB8E00;
vidmem[0] = 'U';
// Put a 'freeze' statement here and see if 'U' is actually printed out
// Besides, gerryg400 gave you some important tips
}
Re: IDT in C
Posted: Sun Oct 23, 2011 8:43 am
by Combuster
You could use attribute(naked) in gcc, but it's not available on all architectures.
Nor is it a proper fix for like, anything.
Re: IDT in C
Posted: Mon Oct 24, 2011 5:10 am
by rdos
berkus wrote:True, an interrupt stub in assembly is the best approach.
Edit: or should I say "thunk" instead. Or "trampoline"?
The stub should be present in the PIC/APIC module already, since it also needs to send EOI to the controller, and methods of doing that differs between PIC/APIC. When linking a new interrupt, it is best to supply an IRQ # and callback address to a registering function. When an interrupt happens, either in the PIC or APIC module, the assembly handler would save registers and call the previous callback function (which could be in C/C++). As the callback returns, registers should be restored and an EOI should be sent before doing an iret.