Page 1 of 1

What does your kernel API look like ?

Posted: Sun Apr 29, 2012 5:12 am
by gerryg400
We have a thread which allows people to show off what their OS looks like. I guess some of us are developing Posix systems and perhaps our APIs look a little like the Posix interface. But others have also given hints and glimpses at what they are doing and it seems a little different. Is there anyone who would like to show off their kernel API ?

I have nothing to show at the moment. Part of the reason I'm asking is because I can't settle on anything so I guess I'm looking for inspiration.

Re: What does your kernel API look like ?

Posted: Sun Apr 29, 2012 5:49 am
by Thomas
Hi gerry,

http://h71000.www7.hp.com/doc/732final/ ... tents.html . You may get inspired by the OpenVMS Api , it is not posixy 8) .

One of the things i like about the api is that it enforces secure coding to some extent , VMS systems support call by descriptor , in which you always specify the pointer and the length all the time.

--Thomas

Re: What does your kernel API look like ?

Posted: Sun Apr 29, 2012 6:00 am
by bluemoon
I have two group of API:
1. driver development kit: this explose some kernel function to driver module by the linker during relocation.
2. application development: I have not design my own yet but POSIX wrapper interface is on my agenda.

I guess the naming of function is not that important at this moment, but I go for something like google code style, for example:

Code: Select all

int syscall_is_computer_up(void);

Re: What does your kernel API look like ?

Posted: Sun Apr 29, 2012 6:14 am
by xenos
My kernel API is not quite finished as well, but here's a glimpse of it:

Since I'm working on a microkernel, the low level kernel API is rather small. Further, it is rather different from POSIX in a number of ways. For example, there is no such thing as fork and exec, but rather things like CreateThread (which creates a new thread within the same address space) and CreateProcess (which executes some program in a new address space), similar to the Windows API. The most important (family of) system call(s) concerns IPC. I plan to implement synchronous IPC only, to avoid the necessity of message queues in the kernel. Finally there are a few support function to query system time, time consumption for threads and processes and so on.

Besides that, I'm thinking about an exokernel approach for the high level interface, although this concept is still a bit new to me...

Re: What does your kernel API look like ?

Posted: Sun Apr 29, 2012 7:25 am
by AndrewAPrice
My kernel API and application API are somewhat intermixed because my entire OS is a platform for running high level bytecode. The closest thing to a "syscall" is calls to methods such as "new System.Threading.Thread" being intercepted during the JIT process. In that way my kernel/application API has been inspired by .Net and Java.

Its hard for someone to be able to run a C program on my platform. Not impossible (my bye code is just another target) but you'll face the same problems as compiling C programs for the JVM.

Re: What does your kernel API look like ?

Posted: Sun Apr 29, 2012 7:25 am
by AndrewAPrice
My kernel API and application API are somewhat intermixed because my entire OS is a platform for running high level bytecode. The closest thing to a "syscall" is calls to methods such as "new System.Threading.Thread" being intercepted during the JIT process. In that way my kernel/application API has been inspired by .Net and Java.

I have no IPC. If two programs reference the same library then only one instance of the library will be loaded. My OS is unconventional but that's why we're doing it!

Its hard for someone to be able to run a C program on my platform. Not impossible (my byte code is just another target) but you'll face the same problems as compiling C programs for the JVM.

Re: What does your kernel API look like ?

Posted: Sun Apr 29, 2012 10:04 am
by Jezze
I've got like two APIs, one for syscalls for userspace programs and one for kernel modules.

Userspace syscalls:

Code: Select all

unsigned int call_open(char *path);
void call_close(unsigned int fd);
unsigned int call_read(unsigned int fd, unsigned int offset, unsigned int count, void *buffer);
unsigned int call_write(unsigned int fd, unsigned int offset, unsigned int count, void *buffer);
unsigned int call_execute(char *path, unsigned int argc, char **argv); // will change this to call_execute(char *path); eventually
unsigned int call_load(char *path); // load module
unsigned int call_unload(char *path);
unsigned int call_exit();
unsigned int call_wait(); // exit without giving up the resource
unsigned int call_attach(int index, void (*routine)()); // attach an event. often used before wait()
unsigned int call_detach(int index);
void call_halt();
void call_reboot();
They are implemented in the simplest way possible like this:

Code: Select all

.intel_syntax noprefix

[...]

.global call_open
call_open:
    mov eax, 0x04
    int 0x80
    ret

[...]

For the kernel modules I've got only a few functions that drivers are able to utilize after they've been relocated. This list will get shorter later on when I will be moving more stuff to userspace.

Code: Select all

irq_register_routine
irq_unregister_routine
mmu_map_kernel_memory
mmu_reload_memory
modules_bus_init
modules_device_init
modules_driver_init
modules_filesystem_init
modules_get_driver
modules_get_filesystem
modules_register_bus
modules_register_device
modules_register_driver
modules_register_filesystem
modules_unregister_bus
modules_unregister_device
modules_unregister_driver
modules_unregister_filesystem
That's it =)

Re: What does your kernel API look like ?

Posted: Mon Apr 30, 2012 1:15 am
by rdos
I have two types of APIs: kernel API and application API.

Unlike many other systems, the kernel and device-drivers are not linked together, but reside in separately compiled modules. This is what the kernel API does. It allows device-drivers to expose their APIs, and to use APIs only be refering to their gate number.

The first step is API exposure, which is done at device-driver initialization time:

Code: Select all

;       AX        Gate number
;       DS:ESI  Gate entry point
;       ES:EDI  Gate name
RegisterOsGate  MACRO
    OsGate register_osgate_nr
                    ENDM
The next step is some device-driver using the function:

Code: Select all


OsGate16    MACRO gate_nr
    db 67h
    db 66h
    db 9Ah
    dd gate_nr
    dw 2
            ENDM

OsGate32    MACRO gate_nr
    db 3Eh
    db 67h
    db 9Ah
    dd gate_nr
    dw 2
            ENDM

OsGate  MACRO gate_nr
IF (size $) EQ 0FF02h
    OsGate16 gate_nr
ELSE
    OsGate32 gate_nr
ENDIF
            ENDM
The user of the code basically loads the parameters into registers and then executes a far call to 0002:gate_nr.

The final step is a kernel function intercepting the protection fault that a call to selector 2 causes, and patching the code to either a far call or a push cs / near call.

The application API is similar, but a little more complex. It allows applications running in V86 mode, 16-bit protected mode and 32-bit protected or flat mode to use the same gate server code.

Below is how the gate exposure looks like.

The simplest interface, which can only be used if the same registers are used from both 16 and 32 bit mode:

Code: Select all


;       AX          Gate number
;       DX          Segment registers to transfer between V86 and protected mode
;       DS:ESI    Gate entry point
;       ES:EDI     Gate name
RegisterBimodalUserGate MACRO
    OsGate register_bimodal_usergate_nr
                        ENDM

This interface is used when different entry points for 16 and 32 bit code are required:

Code: Select all


;       AX          Gate number
;       DX          Segment registers to transfer between V86 and protected mode
;       DS:EBX    16 bit entry point
;       DS:ESI     32 bit entry point
;       ES:EDI     Gate name
RegisterUserGate    MACRO
    OsGate register_usergate_nr
                        ENDM

This interface is used when a module supports the sysenter / sysexit interface, and 16 bit and 32 bit code has the same register usage:

Code: Select all


;       AX      Gate number
;       DX      Segment transfer
;       DS:ESI  Far call address
;       DS:EBP  Near call address
;       ES:EDI  Gate name
RegisterBimodalSyscall MACRO
    OsGate register_bimodal_syscall_nr
                        ENDM

This interface is used when a module supports the sysenter / sysexit interface, and 16 bit and 32 bit code has different register usage:

Code: Select all


;       AX      Gate number
;       DX      Segment transfer
;       DS:EBX  16-bit far call address
;       DS:ESI  32-bit far call address
;       DS:EBP  32-bit near call address
;       ES:EDI  Gate name
RegisterSyscall    MACRO
    OsGate register_syscall_nr
                        ENDM

Even if there are several ways of setting up the server part of application syscalls, there is only one way to use them:

Code: Select all


UserGate16	MACRO gate_nr
    db 67h
    db 66h
    db 9Ah
    dd gate_nr
    dw 1
			ENDM

UserGate32	MACRO gate_nr
    db 3Eh
    db 67h
    db 9Ah
    dd gate_nr
    dw 3
			ENDM

UserGate        MACRO gate_nr
IF (size $) EQ 0FF02h
    UserGate16 gate_nr
ELSE
	UserGate32 gate_nr
ENDIF
			ENDM
If the application is 16-bit it will use a far call to 0001:gate_nr, and if it is 32-bit it will use a far call to 0003:gate_nr. The protection fault handler would then know which destination entry-point to use. Since application calls cannot directly be patched with far calls, the server in kernel will either create a call gate and patch the call to a call gate instead, or if the sysenter interface is supported and the API has registered a sysenter handler, it will patch the code to a call near stub in application space that would load some registers and do a sysenter.

Application calls can also be used from kernel and / or device-drivers. When this is the case, they are patched to far or near calls.

Kernel API (for assembly): http://rdos.net/vc/viewvc.cgi/trunk/ker ... iew=markup
Kernel API (for C): http://rdos.net/vc/viewvc.cgi/trunk/ker ... iew=markup
Application API (for assembly): http://rdos.net/vc/viewvc.cgi/trunk/ker ... iew=markup
Application API (for C): http://rdos.net/vc/viewvc.cgi/trunk/ker ... iew=markup

Calls from V86 mode are special. They use an invalid instruction, which is intercepted, and then the function is carried out in kernel, and iretd is issued to re-execute code after the invalid instruction. However, this interface no longer is maintained, and thus is not working at the moment.

Re: What does your kernel API look like ?

Posted: Mon Apr 30, 2012 9:47 am
by sandras
Jezze wrote: Userspace syscalls:

Code: Select all

unsigned int call_open(char *path);
void call_close(unsigned int fd);
unsigned int call_read(unsigned int fd, unsigned int offset, unsigned int count, void *buffer);
unsigned int call_write(unsigned int fd, unsigned int offset, unsigned int count, void *buffer);
unsigned int call_execute(char *path, unsigned int argc, char **argv); // will change this to call_execute(char *path); eventually
unsigned int call_load(char *path); // load module
unsigned int call_unload(char *path);
unsigned int call_exit();
unsigned int call_wait(); // exit without giving up the resource
unsigned int call_attach(int index, void (*routine)()); // attach an event. often used before wait()
unsigned int call_detach(int index);
void call_halt();
void call_reboot();
What about create/remove?

Re: What does your kernel API look like ?

Posted: Mon Apr 30, 2012 9:55 am
by AndrewAPrice
@rdos: That is a nice clean self-describing API that could be used with very little to no documentation. Well done. :)

Re: What does your kernel API look like ?

Posted: Tue May 01, 2012 12:59 pm
by bluemoon
my new syscall interface reuse the AMD ABI on x86_64:

Code: Select all

; int _exit ();
_exit:
    mov     eax, 1
    syscall
    ret

; int open ( const char * file, int flags, int mode );
open:
    mov     eax, 2
   mov r10, rcx ; rcx is used by syscall
    syscall
    ret
syscall parameters and return values are on rdi, rsi, rcx, etc which are exactly match with the ABI, and only registers needed to be preserve are preserved.
In kernel the stub looks like this:

Code: Select all

_syscall_stub:
    mov     r12, rcx     ; ring3 rip
    mov     r13, r11     ; rflags
    mov     rcx, r10

    ; call handler[eax]

    mov     rcx, r12
    mov     r11, r13
    sysret