Software Interrupts

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
TheChuckster

Software Interrupts

Post by TheChuckster »

Today I tried added software interrupts to my kernel for system calls. I already have IRQs, ISRs, and an IDT working perfectly fine. Like most other kernels, I decided to use int 30h for my interrupt. 30h is 48 in decimal.

What I did was added a isr48 entry to my kernel's IDT:

idt_set_gate(48, (unsigned)isr48, 0x08, 0x8E);

I also added an ISR to my ASM file.

Code: Select all

; 48 : 30h System Call
isr48:
    cli
    push byte 0
    push byte 48
    jmp irq_common_stub

; We call a C function in here. We need to let the assembler know
; that '_fault_handler' exists in another file
extern fault_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
    push ds
    push es
    push fs
    push gs
;    mov ax, 0x10
;    mov ds, ax
;    mov es, ax
;    mov fs, ax
;    mov gs, ax
    mov eax, esp
    push eax
    mov eax, fault_handler
    call eax
    pop eax
    pop gs
    pop fs
    pop es
    pop ds
    popa
    add esp, 8
    iret
Now, I get a general protection fault when I do an int 30h. Calling other interrupts with the int instruction works fine. The commented out lines do not affect the general protection fault at all. I've been debugging this for 2 days, and I'm not one to seek help unless I absolutely cannot figure it out. This is one of those times.

The Bochs debugger wasn't very helpful for me. I couldn't get it to step through the ISR.

Why is it GPFing? I am not doing any weird memory accesses and I have no memory protection schemes besides a simple GDT that maps the entire RAM as being one whole segment both code and data. My paging code maps the entire RAM under supervisor mode. Something must be awry with the interrupt but I've checked the way I'm setting up my IDT at least 20 times. I must be missing something, but what!
AR

Re:Software Interrupts

Post by AR »

How about:

Code: Select all

isr48:
    cli
    push byte 0
    push byte 48
    jmp irq_common_stub

....

    popa
    add esp, 8
    iret
You push 2 bytes but then try to remove 4 from the stack...
User avatar
bubach
Member
Member
Posts: 1223
Joined: Sat Oct 23, 2004 11:00 pm
Location: Sweden
Contact:

Re:Software Interrupts

Post by bubach »

Isn't interrupt 0x32 the first free one? IIRC the ones before that are reserved by Intel.
"Simplicity is the ultimate sophistication."
http://bos.asmhackers.net/ - GitHub
AR

Re:Software Interrupts

Post by AR »

No, that's the first 32 interrupts (non-hex), ie. Interrupt 0x0 through to 0x1F with 0x20 being the first free interrupt.
TheChuckster

Re:Software Interrupts

Post by TheChuckster »

No, that didn't fix it. And the only differences between this interrupt and the others that work are the interrupt number and how it's being raised.
AR

Re:Software Interrupts

Post by AR »

So you tried "add esp, 2"?

Unless the C Handler breaks the stack, there doesn't appear to anything else wrong.
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:Software Interrupts

Post by Pype.Clicker »

AR wrote: So you tried "add esp, 2"?

Unless the C Handler breaks the stack, there doesn't appear to anything else wrong.
@AR: oh! i'm surprised you got fooled by this one. "push byte 0x12" does tell nasm "push immediate value on stack -- oh, the immediate value is so small that i don't feel like using more than one byte to encode it", but a whole 32bits will be used on stack anyway.

That put apart, i'm intrigued by the reason that could lead to make you comment that ES/DS... reset in your handler. In the case of a system call, you *will* have to change segments so that you set them to DPL0...
(hm. i know if one wants to use segment 0x20 from ring3, it actually *must* use selector 0x23. I'm unsure about whether ring0 will be happy with 0x23 or if it wants 0x20)
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:Software Interrupts

Post by Brendan »

Hi,
Pype.Clicker wrote:(hm. i know if one wants to use segment 0x20 from ring3, it actually *must* use selector 0x23. I'm unsure about whether ring0 will be happy with 0x23 or if it wants 0x20)
The rules for accessing data segments are:

A) the CPL (the lowest 2 bits of both CS and SS) and the RPL (the data segment's lowest 3 bits) must both be numerically lower or equal to the descriptors DPL.

B) the CPL (the lowest 2 bits of both CS and SS) must be numerically lower or equal to the RPL (the data segment's lowest 3 bits)

If either of these aren't satisfied, then you'll get a GPF.

For data segments (DS, ES, FS and GS) all CPL=0 code will be happy with RPL = 3 segments, so for a "flat paging" OS it's possible to load the data segment registers with something like 0x23 and never change them for any reason (forget they exist completely). This works fine for compiler generated code, but things like protected mode BIOS interfaces (VBE, APM, etc) and virtual 8086 make a mess of it (it forces you to save and load the data segment registers for IRQ handlers at least, although this is automatic for virtual 8086 IIRC).

The stack segment (SS) is entirely different - the lowest 2 bits of SS must always be equal to the lowest 2 bits of CS.


There are minor things I'd change with the code posted, but nothing I can find that would prevent it from working.

For e.g.:

There is absolutely no reason for the "cli" at the start of the stub. If you actually do want to prevent the kernel from being interrupted then use an interrupt gate (but be very careful that nothing in your kernel stuffs up interrupt latency too badly). IMHO it's better to leave interrupts enabled and then disable them if necessary where necessary.

I'd also change:

Code: Select all

    mov eax, esp
    push eax
    mov eax, fault_handler
    call eax
    pop eax
To the functionally equivelent:

Code: Select all

    push esp
    call fault_handler
    add esp,4
To be honest, I'm not sure why you'd want to call a routine called "fault_handler" when the kernel API isn't a fault (perhaps "call common_isr" might be a better name).

Then I'd be concerned with how common the "isr_common_stub" actually is, but I've always disliked this approach. To me (the kernel API and exception handlers), ignoring the CPU's call table (better known as the IDT) and then implementing your own call table just after it is just a waste of CPU time. It's good for IRQ handlers, but not much else (but I haven't slept for over 20 hours, so I should probably be quite). Anyway, I'd do something like:

Code: Select all

; 48 : 30h System Call
isr48:
    pusha
;    push ds
;    push es
;    push fs
;    push gs
    push esp
    call kernel_API
    add esp,4
;    pop gs
;    pop fs
;    pop es
;    pop ds
    popa
    add esp, 8
    iret
Despite all of this, I still see nothing wrong with the code originally posted (nothing that would cause a GPF). I expect the problem is in the GDT, the IDT, or the "fault_handler" code.


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.
Post Reply