275 lines
12 KiB
ReStructuredText
275 lines
12 KiB
ReStructuredText
.. _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 :ref:`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 :ref:`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 :ref:`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
|