OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Apr 25, 2024 4:56 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 9 posts ] 
Author Message
 Post subject: Problems with split ASM/C++ interrupt handler
PostPosted: Fri Jul 04, 2014 6:43 pm 
Offline
User avatar

Joined: Fri Jul 04, 2014 6:24 pm
Posts: 13
Location: RI
Hello! Advance apologies if this is in the wrong subforum.

I'm trying to write a simple kernel in C++. I just finished writing basic interrupt support (based off of James Molloy's great tutorial - http://www.jamesmolloy.co.uk/tutorial_h ... 20IDT.html), and I'm having trouble with the ISR. I have two parts to it, one in assembly and another in C++.

Assembly, with two macros for err/noerr interrupts and a common stub:
Code:
%macro ISR_NOERR 1
    GLOBAL isr%1
    isr%1:
        cli
        push byte 0
        push byte %1
        jmp ISRTestStub
%endmacro

%macro ISR_ERR 1
    GLOBAL isr%1
    isr%1:
        cli
        push byte %1
        jmp ISRTestStub
%endmacro

ISR_NOERR 0
ISR_NOERR 1
ISR_NOERR 2
ISR_NOERR 3
ISR_NOERR 4
ISR_NOERR 5
ISR_NOERR 6
ISR_NOERR 7
ISR_ERR 8
ISR_NOERR 9
ISR_ERR 10
ISR_ERR 11
ISR_ERR 12
ISR_ERR 13
ISR_ERR 14
ISR_NOERR 15
ISR_NOERR 16
ISR_NOERR 17
ISR_NOERR 18
ISR_NOERR 19
ISR_NOERR 20
ISR_NOERR 21
ISR_NOERR 22
ISR_NOERR 23
ISR_NOERR 24
ISR_NOERR 25
ISR_NOERR 26
ISR_NOERR 27
ISR_NOERR 28
ISR_NOERR 29
ISR_NOERR 30
ISR_NOERR 31

EXTERN handleISR

ISRStub:
    pusha                   

    mov ax, ds             
    push eax               

    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    call handleISR

    pop ebx       
    mov ds, bx
    mov es, bx
    mov fs, bx
    mov gs, bx

    popa                     
    add esp, 8     
    sti
    iret           


And the high-level interrupt handling function:
Code:
typedef struct registers
{
    uint32_t ds;
    uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax;
    uint32_t intNum, errCode;
    uint32_t eip, cs, eflags, useresp, ss;
} registers_t;

void handleISR(registers_t regs)
{
    intvid->printTerminalString("Interrupt!\n");
}


Everything compiles and links fine, but when I trigger a software interrupt (asm volatile ("int 0x03") from the kernelmain) I get a screen filled with "Interrupt!" and endless QEMU resets. Commenting out "call handleISR" prevents this from happening and returns execution to the kernel, but also (obviously) prevents interrupts from being handled. What did I mess up? If anyone needs more info or code I'd be happy to provide it.

Thanks in advance,
John

_________________
LilOS - a beautiful mess


Top
 Profile  
 
 Post subject: Re: Problems with split ASM/C++ interrupt handler
PostPosted: Fri Jul 04, 2014 9:53 pm 
Offline
Member
Member
User avatar

Joined: Sat Jan 15, 2005 12:00 am
Posts: 8561
Location: At his keyboard!
Hi,

When bored, the C/C++ compiler may modify a function's input args. You gave it none, so it could trashing whatever it thinks should have been the input arg (e.g. the copy of DS you're using to restore segment registers).

Note that "void handleISR(registers_t regs)" should be "void handleISR(registers_t *regs)" or the compiler is free to screw up everything in that structure (rather than just modifying a pointer to the structure). Also don't assume that C++ happens to use C calling conventions by accident - e.g. it really should be "extern "C" void handleISR(registers_t *regs)".

Also; the "cli" is not needed. Either you're using "interrupt gates" and the CPU automatically disabled IRQs for you, or you're using "trap gates" and if it's necessary to disable IRQs then it's too late by the time you've reached the "cli". The "sti" is never needed in any case (as the "iret" restores eflags and the interrupt flag).

Finally; the CPU gives you a nice table so you can start the correct exception handler's code, then you have a "common interrupt handler" that destroys the benefits of the IDT (including the ability to have different interrupt/trap/task gates where needed, different "assembly stubs" where needed, etc). Soon you're going to have a slow/stupid "switch(intNum) { ..." to start the correct exception handler (the one that should've been started to begin with). Basically; the entire idea of using a "common interrupt handler" for exceptions is moronic (but sadly hundreds of beginners see a tutorial doing something stupid so we end up with hundreds of beginner's OSs that do the same retarded things; which happens to includes the "pointless cli/sti"). ;)


Cheers,

Brendan

_________________
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.


Top
 Profile  
 
 Post subject: Re: Problems with split ASM/C++ interrupt handler
PostPosted: Sat Jul 05, 2014 4:32 am 
Offline
User avatar

Joined: Fri Jul 04, 2014 6:24 pm
Posts: 13
Location: RI
Hey, it worked! Thanks for the help. It seems that getting rid of cli/sti and making the register_t arg a pointer fixed everything.

Screenshot for your trouble:
Image

On what you said about common interrupt handlers: should I write each for just the original 32 CPU interrupts or every interrupt? The thought of writing individual handlers hadn't occurred to me, but it makes a lot more sense than a common handler #-o.

_________________
LilOS - a beautiful mess


Top
 Profile  
 
 Post subject: Re: Problems with split ASM/C++ interrupt handler
PostPosted: Sat Jul 05, 2014 7:15 am 
Offline
Member
Member
User avatar

Joined: Sat Jan 15, 2005 12:00 am
Posts: 8561
Location: At his keyboard!
Hi,

Mercury1964 wrote:
On what you said about common interrupt handlers: should I write each for just the original 32 CPU interrupts or every interrupt? The thought of writing individual handlers hadn't occurred to me, but it makes a lot more sense than a common handler #-o.


I'd start by writing a general purpose "kernelPanic(optionalData, reason, flags)" function. This would be used when the kernel detects that something is "unrecoverably wrong". For example, if the kernel tries to free a physical page and detects the page is already free, or tries to release a lock that was never acquired, or tries to use an invalid pointer, or whatever else. You could even implement some sort of "kernelAssert(condition, reason, flags)" that would be used just like "assert()" (but less crappy). Initially "kernelPanic()" might just display the details on the screen (e.g. "blue screen of death") but later on it could do other things (e.g. store details somewhere and restart the OS, send the details to a remote machine, etc).

Once that's done; I'd have 32 separate IDT entries for each exception; with different assembly stubs that all just call the "kernelPanic(optionalData, reason, flags)" function for now. This will change later. Also, if the kernel is going to use a software interrupt for its API (e.g. "int 0x80") install that too - it can be a generic thing that returns "E_UNSUPPORTED_FUNCTION" for now (but eventually the assembly stub would do something like "call [kernelAPIfunctionPointerTable + eax*4]").

Next; determine if there are multiple CPUs or not. If there are multiple CPUs install the IDT entries that you'll use for IPIs. The IPI handlers are typically very tiny and don't need any C/C++ at all (e.g. one might just to an INVLPG instruction and nothing else).

Next; write code to determine (e.g. during boot):
  • if the kernel will be using the local APIC (e.g. if a local APIC exists)
  • if the kernel will be using PIC chips or IO APIC
  • what the kernel will be using for timing (RTC, PIT, HPET, ACPI timer, local APIC, TSC)
  • how the kernel will handle FPU errors. Note: for 80486 and later the kernel should enable and use "native FPU error" (where FPU errors are delivered as a normal exception and do not go through the PIC chip's IRQ13).

If the kernel has decided to use local APIC/s, then setup an IDT entry for the local APIC's "spurious IRQ"; plus any other IRQs that come from the local APIC that the kernel will be using (local APIC timer, performance monitoring, thermal monitoring, etc).

If the kernel has decided to use the PIC chips (e.g. there are no IO APICs) then install the PIC chip IRQ handlers; such that:
  • IRQ 0 (PIT) = special IRQ handler for kernel if PIT is used by kernel, else IDT entry left as "not present" and PIT chip IRQ left masked in PIC
  • IRQ 8 (RTC) = special IRQ handler for kernel if RTC is used by kernel, else IDT entry left as "not present" and RTC's IRQ left masked in PIC
  • IRQ 13 (FPU error) = special IRQ handler for kernel if "legacy FPU errors" used by kernel, else IDT entry left as "not present" and IRQ13 left masked in PIC
  • IRQ 2 (cascade) = IDT entry left as "not present"
  • All other PIC chip IRQs get handled by a generic "common PIC IRQ handler". Note that the assembly stubs for IRQ 7 and 15 should detect if the IRQ is a spurious IRQ or not, and if it's not spurious then call the "common PIC IRQ handler", or if it is spurious increment some sort of "number of spurious IRQs" counter instead of calling the "common PIC IRQ handler".
In this case most of your IDT entries (e.g. about 200 of them) won't be used and will be left as "not present".

If the kernel has decided to use the IO APIC/s (instead of PIC chips) then:
  • Install the PIC chip's spurious IRQ handlers (sadly, even with everything masked in the PIC these can still occur). In this case the assembly handling stubs don't need code to determine if the IRQ is spurious or not and they can just increment some sort of "number of spurious IRQs" counter
  • All remaining IRQs get handled by a generic "common IO APIC IRQ handler" (which is not the same as the "common PIC IRQ handler"). This can be done as a loop that checks if each IDT entry is still "not present" in the IDT and replaces them if they are (so you don't need to care much what IDT entries previous kernel initialisation code decided to use)
In this case most of your IDT entries (e.g. about 200 of them) will go to the "common IO APIC IRQ handler".

Note that for a tutorial it's likely that you will cut corners (e.g. only support the PIC chips). In this case I'd still recommend using the general approach described above, but leaving comments in the code for things you skipped. For example; instead of detecting if the kernel will use IO APIC or not you'd have a comment ("// Insert IO APIC detection code here!"), and instead of having code to configure the IDT for IO APIC you'd have a comment ("// Insert code to install IO APIC interrupts here!"). Do not make it impossible for the reader to determine how things should be done in a real OS by simplifying the tutorial too much (e.g. if you have a single "init IDT" function that makes a whole pile of assumptions, then you've created an "anti-tutorial" that teaches the reader how to do things wrong).


Cheers,

Brendan

_________________
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.


Top
 Profile  
 
 Post subject: Re: Problems with split ASM/C++ interrupt handler
PostPosted: Sun Jul 06, 2014 8:07 am 
Offline
Member
Member
User avatar

Joined: Wed Mar 21, 2012 3:01 pm
Posts: 930
If you haven't already, please read this errata: http://wiki.osdev.org/James_Molloy%27s_ ... Known_Bugs


Top
 Profile  
 
 Post subject: Re: Problems with split ASM/C++ interrupt handler
PostPosted: Sun Jul 06, 2014 3:39 pm 
Offline
User avatar

Joined: Fri Jul 04, 2014 6:24 pm
Posts: 13
Location: RI
I have read the errata, right after getting all excited for a really well-written tutorial :( .

_________________
LilOS - a beautiful mess


Top
 Profile  
 
 Post subject: Re: Problems with split ASM/C++ interrupt handler
PostPosted: Thu Oct 23, 2014 3:20 am 
Offline

Joined: Thu Oct 23, 2014 3:05 am
Posts: 2
Hi,

I got the same problem as Mercury had. I commented the cli/sti and made registers_t as a pointer. But still got the same issue. The simulator keep restarting.

The code snippets are below (Actually it is from Molloy's tutorial).

;
; interrupt.s -- Contains interrupt service routine wrappers.
; Based on Bran's kernel development tutorials.
; Rewritten for JamesM's kernel development tutorials.

; This macro creates a stub for an ISR which does NOT pass it's own
; error code (adds a dummy errcode byte).
%macro ISR_NOERRCODE 1
global isr%1
isr%1:
;cli ; Disable interrupts firstly.
push byte 0 ; Push a dummy error code.
push byte %1 ; Push the interrupt number.
jmp isr_common_stub ; Go to our common handler code.
%endmacro

; This macro creates a stub for an ISR which passes it's own
; error code.
%macro ISR_ERRCODE 1
global isr%1
isr%1:
;cli ; Disable interrupts.
push byte %1 ; Push the interrupt number
jmp isr_common_stub
%endmacro

ISR_NOERRCODE 0
ISR_NOERRCODE 1
ISR_NOERRCODE 2
ISR_NOERRCODE 3
ISR_NOERRCODE 4
ISR_NOERRCODE 5
ISR_NOERRCODE 6
ISR_NOERRCODE 7
ISR_ERRCODE 8
ISR_NOERRCODE 9
ISR_ERRCODE 10
ISR_ERRCODE 11
ISR_ERRCODE 12
ISR_ERRCODE 13
ISR_ERRCODE 14
ISR_NOERRCODE 15
ISR_NOERRCODE 16
ISR_NOERRCODE 17
ISR_NOERRCODE 18
ISR_NOERRCODE 19
ISR_NOERRCODE 20
ISR_NOERRCODE 21
ISR_NOERRCODE 22
ISR_NOERRCODE 23
ISR_NOERRCODE 24
ISR_NOERRCODE 25
ISR_NOERRCODE 26
ISR_NOERRCODE 27
ISR_NOERRCODE 28
ISR_NOERRCODE 29
ISR_NOERRCODE 30
ISR_NOERRCODE 31

; In isr.c
extern isr_handler

; This is our common ISR stub. It saves the processor state, sets
; up for kernel mode segments, calls the C-level fault handler,
; and finally restores the stack frame.
isr_common_stub:
pusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax

mov ax, ds ; Lower 16-bits of eax = ds.
push eax ; save the data segment descriptor

mov ax, 0x10 ; load the kernel data segment descriptor
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax

call isr_handler

pop ebx ; reload the original data segment descriptor
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx

popa ; Pops edi,esi,ebp...
add esp, 8 ; Cleans up the pushed error code and pushed ISR number
;sti
iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP

Could someone give me a clue?


Top
 Profile  
 
 Post subject: Re: Problems with split ASM/C++ interrupt handler
PostPosted: Thu Oct 23, 2014 5:23 am 
Offline
Member
Member
User avatar

Joined: Wed Oct 18, 2006 3:45 am
Posts: 9301
Location: On the balcony, where I can actually keep 1½m distance
Sure. By the looks of things, you haven't read this and this yet.

_________________
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]


Top
 Profile  
 
 Post subject: Re: Problems with split ASM/C++ interrupt handler
PostPosted: Thu Oct 23, 2014 8:01 am 
Offline

Joined: Thu Oct 23, 2014 3:05 am
Posts: 2
I have read http://wiki.osdev.org/James_Molloy%27s_Tutorial_Known_Bugs multiple times. But I am not quite sure how to pass the structure as a pointer instead in interrupt.s. Trying to investigate it.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 9 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Amazonbot [bot], Majestic-12 [Bot] and 215 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group