[ Home | Downloads | Software: [ animals | ckport | YIFF ] ]
This document describes YIFF in general as seen by the user of libYIFF. libYIFFc has it's own documentation and is not subject of this document.
YIFF defines a standard structure for all kernel calls. libYIFF provides it as C structure (yiff_call_t). If the process want to call the kernel it fills this structure with the parameters for the call and calls the kernel by calling yiff_call() (or yiff_call_mapped()). libYIFF itself will call yiff_backend_call(). yiff_backend_call() is the CPU specific function doing entering the kernel.
Also there is a way for the kernel to call the process. In order to do this the application tells (as part of the startup process) the kernel the address of a function called masterast. This function is set using yiff_set_masterast(). This is normally done by the c runtime (for example libYIFFc).
This way of being called by the kernel works exactly the same as calling the kernel just othe other way: The kernel calls the masterast function in your process and passes you a full YIFF call structure.
This way the kernel can send signals to your process and similar stuff. As this interface is symmetric this can also be used to implement parts of the interface (some majors, see below) in the user land. Usages for this may be for example filesystems just like FUSE does on GNU/Linux.
The call structure contains of diffrent members. Not all of them needs to be passed between userland and kernel all the time so the CPU depending YIFF backend can do some optimizing on how to pass data (for example using registers vs. memory (stack) passing).
The following table lists all members. "S" in the passing column stands for sender, "R" for receiver and tells if the member needs to be send by the sender or needs to be send by the hypervisor (kernel) to the receiver. This is important for providing a optimized backend function.
Member | Type | Passing (Call) | Passing (Answer) | Description |
---|---|---|---|---|
major | major | S: Yes, R: Yes | S: No, R: No | The major is the ID of the module a function is in while the minor is the ID for the function itself. for example the major may be (the numerical ID for) "io" and the minor (the numerical ID for) "read" for read(). major and minor have sizes so they can be passed using one register on most systems. |
minor | minor | S: Yes, R: Yes | S: No, R: No | |
requestor | pid | S: No, R: Yes | S: No, R: No | This is the process id of the sending process. |
receiver | pid | S: if none zero, R: No | S: No, R: Yes | This is the process id of the receiving process. The value of 0 is used if the request should be handled by the process registered for the given major (normally the kernel). |
flags | flags | S: backend depending, R: Yes | S: backend depending, R: Yes | Flags including information of the argument usage. |
error | sint_least16 | S: No, R: No | S: if non zero, R: Yes | An error generated by the function. This is to set errno or similar. |
arg0, arg1, arg2 | arg | S: depends on flags, R: depends on flags | S: No, R: No | The Arguments for the system call. arg is a union type. Which and how args are used is defined by the flags members settings. |
ret | arg | S: No, R: No | S: depends on flags, R: depends on flags | The return value of the function. |
In addition to the call structure there is the return value for the call function which needs to be passed. It is only passed on answer. libYIFF declares this as yiff_error_t. The type is signed and must be at least 16 bits big. The backend have to convert it to yiff_error_t.
The flags parameter is 16 bits big and stores usage information on the return value and arguments as the following table shows:
Bit | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Reserved for future use, must be zero | YIFFAsyncron | YIFFError | Type of arg2 | Type of arg1 | Type of arg0 | Type of ret |
The YIFFAsyncron flag is used to tell the backend a asyncronus operation should be started. If set the yiff call returns as soon as the call is queued by the hypervisor. The call object must say valid untill the call is complet. The completion of the call is signalized by setting this flag to zero by the backend.
The YIFFError flag is set if the call could not sucessfully be completed. This is the case if the return value of the YIFF call (not of the called function) is not YIFF_ERR_NONE (non zero). This bit can be used by the backend to optimized parameter passing. If not used by the backend libYIFF will set it in case of error.
Type | MSB | LSB |
---|---|---|
Unused | 0 | 0 |
Data | 0 | 1 |
Address | 1 | 0 |
Reserved | 1 | 1 |
The error value is the error happeing within a function. Possible error values are defined in <YIFF/error.h>. This is not used to tell if a function or backend does not exist. For this the return value of the call itself is used. Setting this to YIFF_ERRNO_NOSYS (defined in <YIFF/error.h>) does only make sense if the function exists but some other function the function depends on does not exists. If a function does not exist the call itself should return YIFF_ERR_NOSYS (defined in <YIFF.h>).
The type arg (libYIFF: yiff_arg_t) is a union type with contains the following members:
Member | Type |
---|---|
vp | void * |
reg | register |
ureg | uregister |
sreg | sregister |
major | major |
sint_least16 | sint_least16 |
uint_least16 | uint_least16 |
ssize | ssize |
usize | usize |
id | identifier |
Profiles are variants of YIFF used so that the YIFF specification can match as close as possible the architecture's features and requirements. There are currently 4 diffrent profiles: tiny, small, large and huge. Large is the most used profile. It is targeted for desktop like and similar features systems. Huge is for mainframes, small for small but still powerfull devices like cell phones and tiny is for very small embedded systems.
Basically the rule of thumb is: tiny for 8 bit systems, small for 16 and small 32 bit systems, large for 32 and 64 bit systems and huge for strong 64 bit systems and 'better'. The following table shows the list of architectures for which this is already defined:
Architecture | Register size in bits | Profile |
---|---|---|
AMD64 | strange | Large |
AVR | 8 | Tiny |
Alpha | 64 | Large |
ix86 | strange | Large |
The following data types are defined in YIFF. Values in "()" are recommendations.
Type | Attribues | Size in bits on profile | Description | |||
---|---|---|---|---|---|---|
Tiny | Small | Large | Huge | |||
sregister | signed | 8 | >= 16 | >= 32 | >= 64 | This is the type used for most numerical values. It should have the size of a architecture's general purpose register. |
uregister | unsigned | 8 | >= 16 | >= 32 | >= 64 | The same as sregister just unsigned. |
register | Alias to uregister | |||||
sint_least16 | signed | >= 16 (16) | >= 16 (16) | >= 16 (32) | >= 16 (32/64) | This type is used like sregister just that it has a minimum size of 16 bits. The actual size should be chosen to match the register size and CPU's introduction set. (accessable with single introductions). |
uint_least16 | unsigned | Unsigned version of sint_least16 | ||||
major | signed | 8 | 8 | 16 | 16 | Used to store YIFF major IDs. |
minor | signed | 8 | 8 | 16 | 16 | Used to store YIFF minor IDs. |
ssize | signed | 16 | 32 | 64 | 128 | This is used to store all kind of sizes which may also be negative. It is used by most IO and memory functions to store return values. Not that this size is defined as signed. Unlike POSIX (which does not define ssize_t as signed) this type is abled to store the full range of negative numbers, not only -1. |
usize | unsigned | 16 | 32 | 64 | 128 | Used to store sizes which can not be negative. This is used by most IO and memory functions to store the size of some object in arguements. |
identifier | signed | 16 | 32 | 64 | 128 | This is used for a system-global unique identifier. |
pid | Alias to identifier | This is like identifier but only process IDs are allowed to store in here. | ||||
flags | unsigned | 16 | 16 | 16 | 16 | Used to store flags of YIFF calls. |
error | signed | >= 16 | >= 16 | >= 16 | >= 16 | Used as return type of YIFF calls. |
Major IDs are used in YIFF to select a given module or interface. The Major IDs are unique at runtime. Each Major ID has a corresponding name which is unique, too. The numercal IDs are not defined by specification. They are assigned dynamically by the hypervisor. The only exception is Major ID 0 which is assigned to Major "core". This major is used to get the names for all other majors.
To make this simpler for the developer libYIFF provides a so called mapped mode in which it does the lookup transparently. This is used by yiff_mcall_t and yiff_call_mapped(). However this only works for common modules (see <YIFF.h> for the current list).
Here is a list of common majors with description. The column MM shows if this is available using the mapped interface of libYIFF (as of the time of writing this document).
Major name | MM | Description |
---|---|---|
core | Yes | Basic YIFF functions (mainly needed to lookup other majors and to set masterast). |
memory | Yes | Memory management (allocating, deallocating, locking, ...). |
proc | Yes | Process management (getpid(), kill(), exit(), ...). |
time | Yes | Time functions (time(), alarm(), gettimeofday(), ...). |
io | Yes | General IO functions (read(), write(), close(), ...). |
fs | Yes | Filesystem functions (open(), access(), chown(), ...). |
block | Yes | Block mangement layer (not to be confused with block device raw access which is done using the io major. This is more like Linux' LVM). |
math | Yes | Mathematical support functions. This is for stuff not direct accessable by the CPU like external support devices. |
crypt | Yes | Crypto support functions. Just like major math but for cryptography. |
net | Yes | Network functions (socket(), bind(), connect(), listen(), accept()). |
audio | Yes | Audio Interface. |
scheduler | Yes | Scheduler interface. |
acl | Yes | Permission management. |
identifier | Yes | Allocating and deallocating system global unique idenifiers. |
hypervisor | Yes | Hypervisor interface (registering and unregistering majors, ...). |
sysconf | Yes | System configuration (sysconf(), getnodename(), gethostid(), ...). |
self | No | Loopback to current process. Used for testing. |
sensoractor | Yes | Used to interact with sensors and actors. |
radio | Yes | All kind of Radio devices. |
Not all majors from this list must exist on a given system (the only one that needs to exist is major core (ID=0)). Also there can be any number of additional majors. For example on an embedded system there may be the io major as the devices knows about a serial console and other io resourced but no fs major as it does not have a file system. Such a device also may implement more majors to access device specific resources.
The use of minor IDs are completly depending on the major. However there are some minor IDs wich should be used the same on all majors. If a given major does not impelement what the minor is for it should just not use it. The next table shows the list of those "common minors". Types variate depending on major (but should not).
Name | ID | Argument usage (Type) | Description | |||
---|---|---|---|---|---|---|
return value | arg0 | arg1 | arg2 | |||
NOOP | 0 | (none) | (none) | (none) | (none) | No Operation. |
SET_MASTERAST | 1 | (sregister) status | (void *) masterast function | (none) | (none) | Set masterast function (only on major=core) |
GETMAJORBYNAME / GETOBJECTBYNAME | 2 | (major/identifier) major id or identifier | (void *) major or object name | (none) | (none) | Get major ID or object identifier for major or object by name. (on core: major, else identifier) |
GETMAJORBYID / GETOBJECTBYID | 3 | (sregister) status | (void *) info structure | (major/identifier) major ID or identifier | (none) | Get information about a major or object by ID (on core: major, else object) |
GETMAJORLIST / GETOBJECTLIST | 4 | (ssize) length of list | (void *) list of majors or identifers | (usize) size of list in arg0 | (none) | Get list of all majors or known objects (on core: major, else objects) |
GETMINORBYNAME | 5 | (???) minor | (void *) name of minor | (none | (none | Get a minor by name (only useful on non-standard majors) |
GETMINORBYID | 6 | (sregister) status | (void *) info struct | (???) minor | (none) | Get information about a minor |
GETMINORLIST | 7 | (ssize) length of list | (void *) list of minors | (usize) size of list in arg0 | (none) | Get list of all minors in the major |
(none) | 8-14 | Reserved | ||||
SUBREGISTER | 15 | (identifier) new subobject | (identifier) parent object | Parameters: size, subtype, name,... | Register/Create a new subobject used with this major. | |
REGISTER | 16 | (identifier) new object | Parameters: size, subtype, name,... | Register/Create a new object used with this major. | ||
ALIAS | 17 | (idenifier) alias | (identfier) source id | (none) | (none) | Creates an alias to an object (think of dup()) |
REF | 18 | (sregister) status | (identfier) object | (none) | (none) | Increment refrence counter |
UNREF | 19 | (sregister) status | (identfier) object | (none) | (none) | decrement refrence counter |
STAT | 20 | (sregister) status | (identifer) object | (void *) info structure | (none) | Get information about an object (fstat(), ...) |
STATEX | 21 | (sregister) status | (identifer) object | Like STAT but get's more extended stats (this call may take (much) more time than STAT) | ||
FIX | 22 | (void *) address of object | (identifer) object | (void *) requested address (or NULL) | (none) | Map object into address space |
UNFIX | 23 | (sregister) status | (identifer) object | (void *) address | (none) | Unmap object from address space |
LOCK | 24 | (sregister) status | ? | ? | ? | Lock object in memory (can not be swapped out or similar) |
UNLOCK | 25 | (sregister) status | ? | ? | ? | Unlock object in memory |
* | 26-127 | Depends on major/minor | Usage of those IDs are up to the major | |||
* | 128-32767 | Depends on major/minor | Usage of those IDs are up to the major but are not available/valid on some profiles |
The architecture in this example has 5 64 bit general purpose registers we can use (r0, ..., r4) and uses syscall to enter the kernel. The call struct is in call.
ld r2, &call.arg0 ld r3, &call.arg1 ld r4, &call.arg2 ld r0, &call.major lshift r0, 16 ld r1, &call.minor or r0, r1 lshift r0, 16 ld r1, &call.flags or r0, r1 lshift r0, 32 ld r1, &call.receiver syscall st &call.receiver, r1 st &call.ret, r2 mov r1, r0 and r1, 0x000000000000FFFF rshift r0, 16 mov r2, r0 and r2, 0x0000000000000100 ; YIFFError bit brnz r1, on_error ; BRanch on Not Zero st &call.error, r1 mov r1, r0 and r1, 0x000000000000FFFF st &call.flags, r1 mov returnvaluereg, 0 ret on_error: mov returnvaluereg, r1 ret
yiff_error_t yiff_backend_call(yiff_call_t * call) { static int backend = -1; if ( backend != -1 ) { backend = open("/dev/yiff", O_RDONLY); } if ( backend == -1 ) return YIFF_ERR_NOBKEND; return ioctl(backend, YIFFCALL, call); }
Powered by Fellig.org, Vim and Freedom.