reason wrote:
1. What are the differences between Trap, Interrupt and Task gates? From my understanding the task gate switches the task for you. But I don't get the difference between intterupt and trap gates.
From the Intel IA-32 Architecture Software Developer's Manual, Volume 3, 5.12.1.2:
"The only difference between an interrupt gate and a trap gate is the way the processor handles the IF flga in the EFLAGS register. When accessing an exception or interrupt-handling procesdure through an interrupt gate, the processor clears the IF flag to prevent other interrupts from interferring with the current interrupt handler. A subsequent IRET instruction restores the IF flag to its value in the saved contents of the EFLAGS register on the stack. Accessing a handler procedure through a trap gate does not affect the IF flag."
2. I understand that when you load the IDTR you must give a 16bit size, and then the 32bit memory location. What I don't understand is what is the size measured in? The number of bits of the table, bytes, or is it the number of entries?
The size is measured in bytes--it is the size of your IDT entries minus one byte. Again, Volume 3, 5.10:
"The limit value is expressed in bytes and is added to the base address to get the address of the last valid byte. [snip] the limit should always be one less than an integral multiple of eight (that is, 8N - 1)."
3. For interrupt and trap gates what do I put in the two 16bit offsets? Do I put the first 16bits of the address in the first offset field and the other 16bits in the other offset field?
Yes, something like this:
Code: Select all
void idt_t::set_entry(uint index, uint32 base, uint16 segment, bool present, byte ring)
{
//
// For reference, check Intel architecture's software developer's manual, Volume 3, 5.11
//
ASSERT(index < NUM_IDT_ENTRIES, _T("set_entry: <index> must be less than <NUM_IDT_ENTRIES>."));
ASSERT(ring < 4, _T("set_entry: <ring> must be less than 4."));
idt_entry_t& entry = m_entries[index];
entry.base_low = base & 0xFFFF;
entry.base_high= base>>16;
entry.reserved = IDT_ENTRY_RESERVED;
entry.segment = segment;
entry.ring = ring;
entry.present = present;
}
4. Also what would I put for the segment value? Since grub sets the segment size to 4gb I should just fill this value with 0 right?
The value should be the segment selector for whatever code segment the handlers are to run in (typically, kernel code segment). I don't know what segments GRUB sets up--I recommend you set up your own segments. You'll have to do this sooner or later to get user and system code and data segments, as well as a TSS (Task State Segment--for task switching).
Here is the code I have written so far regarding interrupts
You'd do well to:
1. Make sure your structures are packed. I #define PACKED as follows:
Code: Select all
#define PACKED __attribute__((packed))
...
// Later
struct idt_entry_t
{
} PACKED;
2. Separate the IDT management code into a module of its own.