system calls

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
anon19287473
Member
Member
Posts: 97
Joined: Thu Mar 15, 2007 2:27 pm

system calls

Post by anon19287473 »

Is there any way other than interrupts to return control to the kernel for a system call?
User avatar
omin0us
Member
Member
Posts: 49
Joined: Tue Oct 17, 2006 6:53 pm
Location: Los Angeles, CA
Contact:

Post 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?
http://ominos.sourceforge.net - my kernel
#ominos @ irc.freenode.net
http://dtors.ath.cx - my blog
User avatar
mystran
Member
Member
Posts: 670
Joined: Thu Mar 08, 2007 11:08 am

Post 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.. :)
The real problem with goto is not with the control transfer, but with environments. Properly tail-recursive closures get both right.
User avatar
omin0us
Member
Member
Posts: 49
Joined: Tue Oct 17, 2006 6:53 pm
Location: Los Angeles, CA
Contact:

Post by omin0us »

Yes, I agree with mystran. Implement them with interrupts first. Probably much easier.
http://ominos.sourceforge.net - my kernel
#ominos @ irc.freenode.net
http://dtors.ath.cx - my blog
Crazed123
Member
Member
Posts: 248
Joined: Thu Oct 21, 2004 11:00 pm

Re: system calls

Post 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.
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Post 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).
User avatar
mystran
Member
Member
Posts: 670
Joined: Thu Mar 08, 2007 11:08 am

Post 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.
The real problem with goto is not with the control transfer, but with environments. Properly tail-recursive closures get both right.
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Post by pcmattman »

Really?

That's something I wish I had have known before...
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: system calls

Post 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
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.
User avatar
mystran
Member
Member
Posts: 670
Joined: Thu Mar 08, 2007 11:08 am

Post 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. :)
The real problem with goto is not with the control transfer, but with environments. Properly tail-recursive closures get both right.
Post Reply