Page 1 of 1

system calls

Posted: Thu Apr 19, 2007 6:04 pm
by anon19287473
Is there any way other than interrupts to return control to the kernel for a system call?

Posted: Thu Apr 19, 2007 6:27 pm
by omin0us
Sysenter and Sysexit instructions on x86. This is what most modern Linux kernels and Windows use now instead of int 0x80.
Is there any particular reason you want to avoid using an Interrupt?

Posted: Thu Apr 19, 2007 9:40 pm
by mystran
Well Intel and AMD each have their processor specific "fast" system calls, which work if your idea of memory layout is effectively disabling segments (base 0, limit 4GB segments, one pair kernel, one pair user), so if you use those, you could implement support...

But then again, you probably want to have the interrupt-driven path anyway as a fall-back. Plus since you need interrupt support anyway for dealing with exceptions and hardware interrupts, adding one gate for a system call doesn't make much difference.

I'd say, implement interrupts first, worry about the other methods later.. :)

Posted: Thu Apr 19, 2007 10:38 pm
by omin0us
Yes, I agree with mystran. Implement them with interrupts first. Probably much easier.

Re: system calls

Posted: Fri Apr 20, 2007 9:00 pm
by Crazed123
anon19287473 wrote:Is there any way other than interrupts to return control to the kernel for a system call?
On the x86 architecture you can use call-gates.

Posted: Fri Apr 20, 2007 9:06 pm
by pcmattman
What's wrong with interrupts? The only problem I can think of is needing interrupts enabled (which they won't be during an ISR).

Posted: Fri Apr 20, 2007 9:08 pm
by mystran
pcmattman wrote:What's wrong with interrupts? The only problem I can think of is needing interrupts enabled (which they won't be during an ISR).
Software interrupts aren't masked by IF, so you can use interrupts for system calls whether or not you have hardware interrupts enabled.

Posted: Fri Apr 20, 2007 9:25 pm
by pcmattman
Really?

That's something I wish I had have known before...

Re: system calls

Posted: Fri Apr 20, 2007 9:38 pm
by Brendan
Hi,
anon19287473 wrote:Is there any way other than interrupts to return control to the kernel for a system call?
There's 5 methods...

Software interrupts - short (2 byte) instruction but not the fastest way. Works on all CPUs.

Call gates - longer instruction (7 bytes?) but slightly faster than software interrupts. Works on all CPUs.

Exceptions - instruction length depends on which exception and can be as small as one byte (for e.g. "INT3"). Speed also depends on which exception - slightly slower that software interrupts in general.

SYSCALL - the call itself requires some messing about (i.e. setting return address before use). Is faster, but also only works on some AMD 32-bit CPUs. Works all 64-bit code on any 64-bit CPU, but Intel doesn't support it for 32-bit code running under a 64-bit OS.

SYSENTER - the call itself requires some messing about (i.e. setting return address before use). Is faster, but also only works on some (Intel and AMD) 32-bit CPUs. For 64-bit CPUs Intel support it for 32-bit code and 64-bit code, while AMD support it for 32-bit code only.

Of these options I'd avoid using exceptions unless it's a special purpose API (I've been using INT3 for a "Boot API" in real mode, where no other exception handlers are setup).

For 64-bit code I'd use SYSCALL.

For 32-bit code (including 32-bit code running on a 64-bit OS) I'd support all of the methods above (except for exception handlers) so that code can use software interrupts (if optimised for size), or call gates (if optimized for speed), or SYSCALL or SYSENTER. For systems that don't support SYSCALL and SYSENTER I'd emulate them in the undefined opcode handler, so that they always work (but may be slow), and I'd provide a way for software to determine if they are supported by hardware or not.

Inside the kernel I'd have a call table that is used by everything. For (a rough and broken) example:

Code: Select all

API_softwareInterruptHandler:
    call [API_callTable + eax * 4]
    iretd

API_callGateHandler:
    call [API_callTable + eax * 4]
    retf

API_sysCallHandler:
    call [API_callTable + eax * 4]
    sysret

API_sysEnterHandler:
    call [API_callTable + eax * 4]
    sysexit

undefined_opcode_exception_handler:
   if( user code caused exception) {
       switch( opcode ) {
       case WAS_SYSCALL:
           call [API_callTable + eax * 4]
           iretd
       case WAS_SYSENTER:
           call [API_callTable + eax * 4]
           iretd
       default:
           criticalError(UNDEFINED_OPCODE, USER_MODE):
   } else {
           criticalError(UNDEFINED_OPCODE, KERNEL_MODE):
   }
This means the kernel itself won't care which mechanism is used - it just has functions in the call table that use "RET" to return to whichever handler was used.


Cheers,

Brendan

Posted: Fri Apr 20, 2007 10:57 pm
by mystran
pcmattman wrote:Really?

That's something I wish I had have known before...
Yes, really. Same as exceptions. If you think of it, it makes perfect sense that way. The purpose of the IF-flag is to prevent interrupts when the currently running code can't deal with them. You might not have a valid IDT, or you might not have a valid stack (say when you are switching threads in scheduler). Or you might want to prevent a new interrupt when you've just unmasked PIC but not yet returned, so that you don't end up growing stack by unnecessarily nesting interrupts. Life is also greatly simplified if you can avoid interrupts when you are doing processor synchronization (spinlocks) or some other such situation.

So there's a flag in processor to prevent hardware from interrupting the CPU. There's ofcourse NMI which can't be masked with a simple flag, but you're supposed to be able to deal with NMI at almost any moment, since they are supposedly used to signal hardware failures. Safe to ignore for this discussion.

Now, software interrups and exceptions are never asynchronous: they only occur when the CPU itself does something that causes such a condition to be raised. You can always avoid exceptions (at least theoretically) by checking all the same conditions that processor would check, or simply by avoiding the conditions in the first place. Software interrupts only get raised when an explicit INTn, INTO, INT3 or BOUNDS is executed. As such both exceptions and software interrupts are perfectly predictable, so masking them would be pointless. You might ofcourse want to control whether ring3 code can raise a certain interrupt, but that's what the RPL in IDT is for, and such control needs to be separate from hardware interrupt control anyway, because you want hardware interrupts even in ring3 code which isn't allowed to trigger the same interrupts with INTn.

If you think about it this way, isn't it perfectly logical, after all. :)