doc: move usermode under references
Move usermode documentation to be under api reference and not under kernel. Signed-off-by: Anas Nashif <anas.nashif@intel.com>
This commit is contained in:
parent
46d9d3f6e9
commit
21de8733c6
9 changed files with 1 additions and 1 deletions
274
doc/reference/usermode/kernelobjects.rst
Normal file
274
doc/reference/usermode/kernelobjects.rst
Normal file
|
@ -0,0 +1,274 @@
|
|||
.. _kernelobjects:
|
||||
|
||||
Kernel Objects
|
||||
##############
|
||||
|
||||
A kernel object can be one of three classes of data:
|
||||
|
||||
* A core kernel object, such as a semaphore, thread, pipe, etc.
|
||||
* A thread stack, which is an array of :c:type:`struct _k_thread_stack_element`
|
||||
and declared with :c:macro:`K_THREAD_STACK_DEFINE()`
|
||||
* A device driver instance (struct device) that belongs to one of a defined
|
||||
set of subsystems
|
||||
|
||||
The set of known kernel objects and driver subsystems is defined in
|
||||
include/kernel.h as :cpp:enum:`k_objects`.
|
||||
|
||||
Kernel objects are completely opaque to user threads. User threads work
|
||||
with addresses to kernel objects when making API calls, but may never
|
||||
dereference these addresses, doing so will cause a memory protection fault.
|
||||
All kernel objects must be placed in memory that is not accessible by
|
||||
user threads.
|
||||
|
||||
Since user threads may not directly manipulate kernel objects, all use of
|
||||
them must go through system calls. In order to perform a system call on
|
||||
a kernel object, checks are performed by system call handler functions
|
||||
that the kernel object address is valid and that the calling thread
|
||||
has sufficient permissions to work with it.
|
||||
|
||||
Permission on an object also has the semantics of a reference to an object.
|
||||
This is significant for certain object APIs which do temporary allocations,
|
||||
or objects which themselves have been allocated from a runtime memory pool.
|
||||
|
||||
If an object loses all references, two events may happen:
|
||||
|
||||
* If the object has an associated cleanup function, the cleanup function
|
||||
may be called to release any runtime-allocated buffers the object was using.
|
||||
|
||||
* If the object itself was dynamically allocated, the memory for the object
|
||||
will be freed.
|
||||
|
||||
Object Placement
|
||||
****************
|
||||
|
||||
Kernel objects that are only used by supervisor threads have no restrictions
|
||||
and can be located anywhere in the binary, or even declared on stacks. However,
|
||||
to prevent accidental or intentional corruption by user threads, they must
|
||||
not be located in any memory that user threads have direct access to.
|
||||
|
||||
In order for a static kernel object to be usable by a user thread via system
|
||||
call APIs, several conditions must be met on how the kernel object is declared:
|
||||
|
||||
* The object must be declared as a top-level global at build time, such that it
|
||||
appears in the ELF symbol table. It is permitted to declare kernel objects
|
||||
with static scope. The post-build script ``gen_kobject_list.py`` scans the
|
||||
generated ELF file to find kernel objects and places their memory addresses
|
||||
in a special table of kernel object metadata. Kernel objects may be members
|
||||
of arrays or embedded within other data structures.
|
||||
|
||||
* Kernel objects must be located in memory reserved for the kernel. They
|
||||
must not be located in any memory partitions that are user-accessible.
|
||||
|
||||
* Any memory reserved for a kernel object must be used exclusively for that
|
||||
object. Kernel objects may not be members of a union data type.
|
||||
|
||||
Kernel objects that are found but do not meet the above conditions will not be
|
||||
included in the generated table that is used to validate kernel object pointers
|
||||
passed in from user mode.
|
||||
|
||||
The debug output of the ``gen_kobject_list.py`` script may be useful when
|
||||
debugging why some object was unexpectedly not being tracked. This
|
||||
information will be printed if the script is run with the ``--verbose`` flag,
|
||||
or if the build system is invoked with verbose output.
|
||||
|
||||
Dynamic Objects
|
||||
***************
|
||||
|
||||
Kernel objects may also be allocated at runtime if
|
||||
:option:`CONFIG_DYNAMIC_OBJECTS` is enabled. In this case, the
|
||||
:cpp:func:`k_object_alloc()` API may be used to instantiate an object from
|
||||
the calling thread's resource pool. Such allocations may be freed in two
|
||||
ways:
|
||||
|
||||
* Supervisor threads may call :cpp:func:`k_object_free()` to force a dynamic
|
||||
object to be released.
|
||||
|
||||
* If an object's references drop to zero (which happens when no threads have
|
||||
permissions on it) the object will be automatically freed. User threads
|
||||
may drop their own permission on an object with
|
||||
:cpp:func:`k_object_release()`, and their permissions are automatically
|
||||
cleared when a thread terminates. Supervisor threads may additionally
|
||||
revoke references for another thread using
|
||||
:cpp:func:`k_object_access_revoke()`.
|
||||
|
||||
Because permissions are also used for reference counting, it is important for
|
||||
supervisor threads to acquire permissions on objects they are using even though
|
||||
the access control aspects of the permission system are not enforced.
|
||||
|
||||
Implementation Details
|
||||
======================
|
||||
|
||||
The ``gen_kobject_list.py`` script is a post-build step which finds all the
|
||||
valid kernel object instances in the binary. It accomplishes this by parsing
|
||||
the DWARF debug information present in the generated ELF file for the kernel.
|
||||
|
||||
Any instances of structs or arrays corresponding to kernel objects that meet
|
||||
the object placement criteria will have their memory addresses placed in a
|
||||
special perfect hash table of kernel objects generated by the 'gperf' tool.
|
||||
When a system call is made and the kernel is presented with a memory address
|
||||
of what may or may not be a valid kernel object, the address can be validated
|
||||
with a constant-time lookup in this table.
|
||||
|
||||
Drivers are a special case. All drivers are instances of :c:type:`struct
|
||||
device`, but it is important to know what subsystem a driver belongs to so that
|
||||
incorrect operations, such as calling a UART API on a sensor driver object, can
|
||||
be prevented. When a device struct is found, its API pointer is examined to
|
||||
determine what subsystem the driver belongs to.
|
||||
|
||||
The table itself maps kernel object memory addresses to instances of
|
||||
:c:type:`struct _k_object`, which has all the metadata for that object. This
|
||||
includes:
|
||||
|
||||
* A bitfield indicating permissions on that object. All threads have a
|
||||
numerical ID assigned to them at build time, used to index the permission
|
||||
bitfield for an object to see if that thread has permission on it. The size
|
||||
of this bitfield is controlled by the :option:`CONFIG_MAX_THREAD_BYTES`
|
||||
option and the build system will generate an error if this value is too low.
|
||||
* A type field indicating what kind of object this is, which is some
|
||||
instance of :cpp:enum:`k_objects`.
|
||||
* A set of flags for that object. This is currently used to track
|
||||
initialization state and whether an object is public or not.
|
||||
* An extra data field. This is currently used for thread stack objects
|
||||
to denote how large the stack is, and for thread objects to indicate
|
||||
the thread's index in kernel object permission bitfields.
|
||||
|
||||
Dynamic objects allocated at runtime are tracked in a runtime red/black tree
|
||||
which is used in parallel to the gperf table when validating object pointers.
|
||||
|
||||
Supervisor Thread Access Permission
|
||||
***********************************
|
||||
|
||||
Supervisor threads can access any kernel object. However, permissions for
|
||||
supervisor threads are still tracked for two reasons:
|
||||
|
||||
* If a supervisor thread calls :cpp:func:`k_thread_user_mode_enter()`, the
|
||||
thread will then run in user mode with any permissions it had been granted
|
||||
(in many cases, by itself) when it was a supervisor thread.
|
||||
|
||||
* If a supervisor thread creates a user thread with the
|
||||
:c:macro:`K_INHERIT_PERMS` option, the child thread will be granted the
|
||||
same permissions as the parent thread, except the parent thread object.
|
||||
|
||||
User Thread Access Permission
|
||||
*****************************
|
||||
|
||||
By default, when a user thread is created, it will only have access permissions
|
||||
on its own thread object. Other kernel objects by default are not usable.
|
||||
Access to them needs to be explicitly or implicitly granted. There are several
|
||||
ways to do this.
|
||||
|
||||
* If a thread is created with the :c:macro:`K_INHERIT_PERMS`, that thread
|
||||
will inherit all the permissions of the parent thread, except the parent
|
||||
thread object.
|
||||
|
||||
* A thread that has permission on an object, or is running in supervisor mode,
|
||||
may grant permission on that object to another thread via the
|
||||
:c:func:`k_object_access_grant()` API. The convenience function
|
||||
:c:func:`k_thread_access_grant()` may also be used, which accepts a
|
||||
NULL-terminated list of kernel objects and calls
|
||||
:c:func:`k_object_access_grant()` on each of them. The thread being granted
|
||||
permission, or the object whose access is being granted, do not need to be in
|
||||
an initialized state. If the caller is from user mode, the caller must have
|
||||
permissions on both the kernel object and the target thread object.
|
||||
|
||||
* Supervisor threads may declare a particular kernel object to be a public
|
||||
object, usable by all current and future threads with the
|
||||
:c:func:`k_object_access_all_grant()` API. You must assume that any
|
||||
untrusted or exploited code will then be able to access the object. Use
|
||||
this API with caution!
|
||||
|
||||
* If a thread was declared statically with :c:macro:`K_THREAD_DEFINE()`,
|
||||
then the :c:macro:`K_THREAD_ACCESS_GRANT()` may be used to grant that thread
|
||||
access to a set of kernel objects at boot time.
|
||||
|
||||
Once a thread has been granted access to an object, such access may be
|
||||
removed with the :c:func:`k_object_access_revoke()` API. This API is not
|
||||
available to user threads, however user threads may use
|
||||
:c:func:`k_object_release()` to relinquish their own permissions on an
|
||||
object.
|
||||
|
||||
API calls from supervisor mode to set permissions on kernel objects that are
|
||||
not being tracked by the kernel will be no-ops. Doing the same from user mode
|
||||
will result in a fatal error for the calling thread.
|
||||
|
||||
Objects allocated with :cpp:func:`k_object_alloc()` implicitly grant
|
||||
permission on the allocated object to the calling thread.
|
||||
|
||||
Initialization State
|
||||
********************
|
||||
|
||||
Most operations on kernel objects will fail if the object is considered to be
|
||||
in an uninitialized state. The appropriate init function for the object must
|
||||
be performed first.
|
||||
|
||||
Some objects will be implicitly initialized at boot:
|
||||
|
||||
* Kernel objects that were declared with static initialization macros
|
||||
(such as :c:macro:`K_SEM_DEFINE` for semaphores) will be in an initialized
|
||||
state at build time.
|
||||
|
||||
* Device driver objects are considered initialized after their init function
|
||||
is run by the kernel early in the boot process.
|
||||
|
||||
If a kernel object is initialized with a private static initializer, the
|
||||
object must have :c:func:`_k_object_init()` called on it at some point by a supervisor
|
||||
thread, otherwise the kernel will consider the object uninitialized if accessed
|
||||
by a user thread. This is very uncommon, typically only for kernel objects that
|
||||
are embedded within some larger struct and initialized statically.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct foo {
|
||||
struct k_sem sem;
|
||||
...
|
||||
};
|
||||
|
||||
struct foo my_foo = {
|
||||
.sem = _K_SEM_INITIALIZER(my_foo.sem, 0, 1),
|
||||
...
|
||||
};
|
||||
|
||||
...
|
||||
_k_object_init(&my_foo.sem);
|
||||
...
|
||||
|
||||
|
||||
Creating New Kernel Object Types
|
||||
********************************
|
||||
|
||||
When implementing new kernel features or driver subsystems, it may be necessary
|
||||
to define some new kernel object types. There are different steps needed
|
||||
for creating core kernel objects and new driver subsystems.
|
||||
|
||||
Creating New Core Kernel Objects
|
||||
================================
|
||||
|
||||
* In ``scripts/gen_kobject_list.py``, add the name of the struct to the
|
||||
:py:data:`kobjects` list.
|
||||
|
||||
Instances of the new struct should now be tracked.
|
||||
|
||||
Creating New Driver Subsystem Kernel Objects
|
||||
============================================
|
||||
|
||||
All driver instances are :c:type:`struct device`. They are differentiated by
|
||||
what API struct they are set to.
|
||||
|
||||
* In ``scripts/gen_kobject_list.py``, add the name of the API struct for the
|
||||
new subsystem to the :py:data:`subsystems` list.
|
||||
|
||||
Driver instances of the new subsystem should now be tracked.
|
||||
|
||||
Configuration Options
|
||||
*********************
|
||||
|
||||
Related configuration options:
|
||||
|
||||
* :option:`CONFIG_USERSPACE`
|
||||
* :option:`CONFIG_MAX_THREAD_BYTES`
|
||||
|
||||
API Reference
|
||||
*************
|
||||
|
||||
.. doxygengroup:: usermode_apis
|
||||
:project: Zephyr
|
Loading…
Add table
Add a link
Reference in a new issue