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.