Page 1 of 2
Interrupts seem to be an issue...
Posted: Sat May 06, 2017 7:55 pm
by TheAlmostGenius
Mostly just a rant but I have been having issues with getting any interrupts working. There is no interrupt activity at all, as if interrupts are disabled or there is a triple fault.
I have tried the following:
OSDev ISRs and implementation.
My own ISR implementation.
Changing the way IDT entries are encoded. (Just in case it was an issue... It wasn't.)
Implementing ISRs using the GCC interrupt attribute.
Double checking interrupts are actually enabled. (They are.)
Any input will be appreciated and tested as long as I haven't tried it and forgotten to add it to the list above. This is mostly for other people who may bump into the same problem(s) and keeping people posted if/when I manage to fix it.
Re: Interrupts seem to be an issue...
Posted: Sat May 06, 2017 10:58 pm
by Love4Boobies
We can't really answer your question because it is, as you say, just a rant rather than a problem description. We have no idea what you actually tried and how you went about it (e.g., did you just write ISRs or did you also add them to the IDT?). The two diagnostics you came up with have very different symptoms (no interrupts vs triple fault) --- there is never a situation where you're not sure which is the case. Come back with more details.
Re: Interrupts seem to be an issue...
Posted: Sun May 07, 2017 12:30 am
by davidv1992
From the scant few details your post supplies, you seem to be missing an important point: Interrupts don't happen on their own.
Interrupts are either generated by your code (through the int instruction), and these cannot be blocked through the sti/cli instructions. As you have full control over these, they are awesome for checking whether you set up your IDTs and ISRs correctly
The second source of interrupts is from the external interrupt line of the processor. This is typically (on x86) controlled by an interrupt controller, which only generates interrupts when a device asks it to, and will typically require some form of end of interrupt signal to be send after each interrupt for the next to be sent.
In other words, there is no "magic" source of interrupts that will trigger once you run an sti instruction. It can be the case that some previous stage of the booting code has left the hardware in a state that the interrupt controller will send one, but there is no guarantee of that happening.
Re: Interrupts seem to be an issue...
Posted: Mon May 08, 2017 4:57 pm
by TheAlmostGenius
I will post certain pieces of code as time permits. (Very busy at the moment.)
I have IDTs set-up with the following code:
idt.c:
Code: Select all
/*
* idt.c
* x86 interrupt descriptor table definition and initialization.
* For WolfNet OS
* By John Wolfe 2015
*/
/* LibC headers. */
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* LibK headers. */
#include <arch.h>
#include <kernelint.h>
#include <core/interrupts.h>
#include <drivers/virt_ic.h>
/*
* Name Bit Full Name Description
* Offset 48..63 Offset 16..31 Higher part of the offset.
* P 47 Present Set to 0 for unused interrupts.
* DPL 45,46 Descriptor Privilege Level Gate call protection. Specifies the lowest privilege Level the caller can have. So hardware and CPU interrupts can be protected from being called out of userspace.
* S 44 Storage Segment Set to 0 for interrupt gates (see below).
* Type 40..43 Gate Type 0..3
* IDT gate types:
* 0b0101 0x5 32 bit task gate
* 0b0110 0x6 16-bit interrupt gate
* 0b0111 0x7 16-bit trap gate
* 0b1110 0xE 32-bit interrupt gate
* 0b1111 0xF 32-bit trap gate
* 0 32..39 Unused 0..7 Have to be 0.
* Selector 16..31 Selector 0..15 Selector of the interrupt function (to make sense - the kernel's selector). The selector's descriptor's DPL field has to be 0.
* Offset 0..15 Offset 0..15 Lower part of the interrupt function's offset address (also known as pointer).
*/
static inline void idt_defineidtr(void*, kernel_uint16_t);
void idt_initialize(void)
{
printf("Initializing IDT...\n");
for (int i = 0; i < (IDT_ENTRIES - 1); ++i) {
memset(&idt[i], '0', sizeof(idt_table_t));
}
#ifdef __debug_kernel
printf("sizeof(idt): %u bytes.\n", (unsigned int) sizeof(idt));
printf("sizeof idt_table_t: %u bytes.\n", (unsigned int) sizeof(idt_table_t));
printf("idt_table_t.offset_1 offset: %u bytes.\n", (unsigned int) offsetof(idt_table_t, offset_1));
printf("idt_table_t.selector offset: %u bytes.\n", (unsigned int) offsetof(idt_table_t, selector));
printf("idt_table_t.zero offset: %u bytes.\n", (unsigned int) offsetof(idt_table_t, zero));
printf("idt_table_t.type_attr offset: %u bytes.\n", (unsigned int) offsetof(idt_table_t, type_attr));
printf("idt_table_t.offset_2 offset: %u bytes.\n", (unsigned int) offsetof(idt_table_t, offset_2));
#endif
idt_defineidtr(&idt, sizeof(idt));
printf("IDT initialization complete!\n");
}
void idt_encode_entry(void* idt_entry, kernel_uint16_t selector, void* offset, kernel_uint8_t gate_type, kernel_uint8_t priv_level, kernel_uint8_t used_bit)
{
idt_table_t* new_entry = (idt_table_t*) idt_entry;
kernel_uint32_t pointer = (kernel_uint32_t) offset;
new_entry->offset_1 = (kernel_uint16_t) pointer & 0xFFFF;
new_entry->offset_2 = (kernel_uint16_t) pointer >> 16;
new_entry->selector = selector; /* NOTE: Minimum privilege for calling process. */
new_entry->zero = 0; /* NOTE: THIS MUST NEVER BE CHANGED! */
new_entry->type_attr = gate_type & 0x0F;
new_entry->type_attr |= (priv_level & 0x03) << 5;
if (used_bit == 1) {
new_entry->type_attr |= 0x80;
}
else if (used_bit == 0) {
new_entry->type_attr &= ~(0x80);
}
if (gate_type == 0xE || gate_type == 0x6) {
new_entry->type_attr &= ~(0x10); /* NOTE: This is always set to zero for interrupt gates. */
}
}
/* IDT definition function. */
static inline void idt_defineidtr(void* base, kernel_uint16_t size)
{
typedef struct lidt_data {
kernel_uint16_t length;
kernel_uint32_t base;
} __attribute__((packed)) lidt_data_t;
lidt_data_t lidt_data;
lidt_data.length = size;
lidt_data.base = (kernel_uint32_t) base;
if (offsetof(lidt_data_t, base) != 2) {
printf("IDT Error: LIDT base offset = %u\n", (unsigned int) offsetof(lidt_data_t, base));
kernel_exit();
}
#ifdef __debug_kernel
printf("LIDT length offset = %u\n", (unsigned int) offsetof(lidt_data_t, length));
printf("LIDT length base = %u\n", (unsigned int) offsetof(lidt_data_t, base));
#endif
__asm__ __volatile__ ("lidt (%0)"::"r"(&lidt_data));
}
Because my most recent try was using the ISRs PIC and Multitasking method each entry is initialized in interrupts_initialize() by:
Code: Select all
idt_encode_entry(idt+32, KERNEL_CODE_DESCRIPTOR, irq0handler, INTERRUPT_GATE, 0, 1);
arch.h:
This points to x86.h on the x86 and x86_64 (x86_64 is unimplemented.)
x86.h:
Code: Select all
/* IDT. */
void idt_initialize(void);
void idt_encode_entry(void*, kernel_uint16_t, void *, kernel_uint8_t, kernel_uint8_t, kernel_uint8_t);
Interrupt handlers defined in 8259_pic.h, this file (during it's init routine) install the functions to function pointers in virt_ic.h
The handlers are defined in interrupts_init.c where the idt entries are encoded using the method described in ISRs PIC and Multitasking.
If any more information is required, feel free to PM me for a tarball or more snippets.
Re: Interrupts seem to be an issue...
Posted: Mon May 08, 2017 5:41 pm
by eryjus
Code: Select all
idt_defineidtr(idt, sizeof(idt) - 1);
size or length is really sizeof()-1, with an unsigned range from 0x0000-0xffff.
Therefore, 1 entry would have a length of 7; 2 entries a length of 0x0f; etc.
Re: Interrupts seem to be an issue...
Posted: Tue May 09, 2017 5:01 pm
by TheAlmostGenius
For the source of the interrupts; I have programmed the PIT and enabled IRQ0 in the PIC. Interrupts are then enabled later on in the initialization.
sizeof(idt) returns 2048 bytes?
My idt_entry_t is 8 bytes for each one.
This means I have 256 entries and idtr is passed a limit of 2047. Is this correct or is this where my error lies?
Re: Interrupts seem to be an issue...
Posted: Tue May 09, 2017 5:18 pm
by LtG
TheAlmostGenius wrote:I think I see the problem. From what my insomnia plagued mind can gather, the size that my code provides is effectively constant because it's the size of 1 IDT entry - 1 whereas it needs to be ((sizeof(IDT_ENTRY )- 1) * NO_OF_ENTRIES)?
As for the source of the interrupts; I have programmed the PIT and enabled IRQ0 in the PIC. Interrupts are then enabled later on in the initialization.
I'm not sure if I understand you, but if I do, then no. First few answers:
1 IDT entry in the IDT: 7 (8 - 1)
2 IDT entry in the IDT: 15 ((2*8) - 1)
3 IDT entry in the IDT: 23 ((3*8) - 1)
...
The IDTR (where you write the address of the IDT and the _LIMIT_, not SIZE) want's the LIMIT, this limit tells the CPU the IDT's address's LIMIT. Limit is different than size or length, different by one. Basically the CPU will use the base address (the address you write into IDTR) as the start of the IDT and it will add to that the LIMIT and that will be the last _valid_ byte in the IDT, thus it's [BASE; BASE + LIMIT], inclusive on both sides.
You should read the Intel manual (it's the best resource and if you don't care about newer features the 386 version is quite short and easy to understand) as well as the Wiki:
http://wiki.osdev.org/Interrupt_Descriptor_Table
And if it wasn't clear, your suggested version "((sizeof(IDT_ENTRY )- 1) * NO_OF_ENTRIES)" would produce:
1 IDT entry in the IDT: 7 ((8 - 1) * 1) "correct", well, accidentally correct value but wrong way to calculate it
2 IDT entry in the IDT: 14 ((8 - 1) * 2) WRONG
3 IDT entry in the IDT: 21 ((8 - 1) * 3) WRONG
...
Re: Interrupts seem to be an issue...
Posted: Tue May 09, 2017 5:30 pm
by LtG
TheAlmostGenius wrote:For the source of the interrupts; I have programmed the PIT and enabled IRQ0 in the PIC. Interrupts are then enabled later on in the initialization.
sizeof(idt) returns 2048 bytes?
My idt_entry_t is 8 bytes for each one.
This means I have 256 entries and idtr is passed a limit of 2047. Is this correct or is this where my error lies?
I guess you edited your post... so let's reply to this one now =)
"sizeof(idt) returns 2048 bytes?", what are you asking? Are you asking me if the idt in your code is 2KiB? I don't know =)
8*256 = 2048
So if you want to fill out all 256 IDT entries then it would be 2KiB in size and then you would give a LIMIT of 2047 and that would be correct. Note, unless you actually use all 256 entries there's not much point adding all of them there either. It's up to you, both ways work and there's very little difference between them, apart from the fact that if you add all 256 and only use, say 40-60, then you need to be able to handle those rest if they can be called from userspace (which you shouldn't allow except for your syscall if you use INT's for that).
Remember, you also need to remap the PIC. The first part of you message only says enabling the IRQ0, you also need to remap it in the PIC. I would say _MUST_ remap it, but technically it's not a MUST, but a very strong NEED/SHOULD. If you don't remap it then it will overlap with the x86 CPU exceptions and you have no good way of knowing whether you actually received hardware IRQ0 or a #DF (IIRC), lookup 8259 PIC in the Wiki for more details on how to remap if you haven't already.
Re: Interrupts seem to be an issue...
Posted: Tue May 09, 2017 6:00 pm
by TheAlmostGenius
I am reading parts 6.10 and 6.11 in volume 3a of Intel64 and IA-32 Architectures Software Developer's manual.
I have enabled the PIC and remapped it to entry 32 as recommended on the 8259 PIC page on the wiki.
From the sounds of it, my mistake is filling all entries as NULL and then filling in the ones I am using. If that's the case, I will add a small warning on the IDT page so others don't make the same mistake.
Re: Interrupts seem to be an issue...
Posted: Tue May 09, 2017 6:09 pm
by LtG
TheAlmostGenius wrote:
I have enabled the PIC and remapped it to entry 32 as recommended on the 8259 PIC page on the wiki.
From the sounds of it, my mistake is filling all entries as NULL and then filling in the ones I am using. If that's the case, I will add a small warning on the IDT page so others don't make the same mistake.
Filling all of them to null and then filling out the ones you are using works just fine, no issue there, though there's no benefit for having null values, so why do it? But you can, if you want to. Btw, by null I assume you mean the number 0? And that you either use a 8B (64-bit) 0 or two 4B (32-bit) 0's, right? If the value is fully 0 (8B or 2x4B) then the present (P) bit is 0 (not present) and DPL is zero also, so user space can't call them.
If you haven't already, add all the exception (exception, not interrupts) handlers (simple handlers, print exception number and panic/halt) and see what happens. If that doesn't help you could add all 256 ISR handlers (exceptions and interrupts) and see if that helps you find out where you problem is.
Have you tried debugging with GDB or similar?
I'm a bit unclear what your current issue exactly is, triple fault causing reboot? Something else? Once you describe the problem clearly we're better able to help, though it might also help you to solve it as well =)
Also I would test the interrupts work with soft INT's (the INT op-code) before attempting to get real interrupts (IRQ's) working, that way you know your IDT, etc is valid, it will help you to concentrate on the right part of your code.
Re: Interrupts seem to be an issue...
Posted: Tue May 09, 2017 7:56 pm
by TheAlmostGenius
I have to assume it's the IDT as there are no interrupts being fired (soft or hard). There was interrupt activity (Triple fault) when I used my own implementation, this was caused by the timer interrupt. At the time it was the only implemented interrupt. All others now print exit code and error message with the exception of the hardware interrupts which are NULL except for the timer interrupt, which prints a nice "Timer fired!\n" message and continues execution.
Oh and by NULL, I do mean 0.
edit:
When I try to syscall, QEMU pauses itself.
Re: Interrupts seem to be an issue...
Posted: Tue May 09, 2017 10:01 pm
by LtG
TheAlmostGenius wrote:I have to assume it's the IDT as there are no interrupts being fired (soft or hard). There was interrupt activity (Triple fault) when I used my own implementation, this was caused by the timer interrupt. At the time it was the only implemented interrupt. All others now print exit code and error message with the exception of the hardware interrupts which are NULL except for the timer interrupt, which prints a nice "Timer fired!\n" message and continues execution.
Oh and by NULL, I do mean 0.
edit:
When I try to syscall, QEMU pauses itself.
How do you know there are no "hard interrupts" (IRQs) being fired? Or do you mean interrupts are disabled and thus ignored, but possibly still fired? With this stuff it pays to be accurate and precise.
How is syscall related to this? And what do you mean by syscall, do you mean the "SYSCALL" op-code, syscall's in general, or? Can't really see the relevance...
It would really help if you explained most importantly what the problem is, you only say that there used to be a triple fault, not what the problem is now, nor what are you doing prior to the triple fault. You can use debugging (better) or just inserting a halt instruction into your code and seeing for far you get without issues and thus identify what op-code is causing the problems..
Re: Interrupts seem to be an issue...
Posted: Tue May 09, 2017 11:07 pm
by TheAlmostGenius
I am using QEMU to test atm and every time I use
QEMU pauses itself and refuses to unpause. I'm assuming this is an error of some type (OS, CPU emulation or general QEMU) but I am pretty sure that no interrupts are being fired as all are unmasked in the PIC, interrupts are enabled and there are entries in the IDT for the timer interrupt. The PIT is initialized and interrupts are enabled at the end. I would expect interrupts from the timer a few times a second but none show up. Syscalls weren't showing before either, but now they pause QEMU unrecoverably. When the interrupts were causing triple fault, I was using a different implementation that has since been lost. (Server errors.)
My syscall vector is 0x80.
Re: Interrupts seem to be an issue...
Posted: Wed May 10, 2017 9:20 am
by eryjus
TheAlmostGenius wrote:QEMU pauses itself and refuses to unpause.
Are you sure you are not executing a 'hlt' with the interrupts flag clear? I seriously doubt it's a QEMU bug.
You might want to download and build Bochs, which will be able to give you more information about the CPU state. Also, try the command line option for QEMU: "-d guest_errors". Also, "-d asm_in" will give you a trace of the code executed.
Re: Interrupts seem to be an issue...
Posted: Wed May 10, 2017 1:23 pm
by TheAlmostGenius
After the syscall:
there is a while loop. Could it be that GCC has optimised this to a hlt instruction? Maybe there is a hlt generated by the compiler. I do know however that I haven't added one in myself. I will try bochs as it is hailed as the better choice for debugging. If it is due to a hlt instruction the compiler has placed in the code, this would mean the int instruction doesn't execute my syscall code.