Kernel Stabilization

When starting a new project, goals on the behavior and usage are set. Throughout development new learning impact these goals. Hence, the API, the internals and toolchain requirements change. Over time, breaking changes should subside and lead to a stable kernel.

API

The kernel API core elements are the same as in the first prototype. The API is based on existing Rust software, namely the standard library and tokio [57] framework. An RTOS runs on resource-constrained devices. E.g., additional parameters such as stack size are required when spawning a thread on an embedded device. During the thesis, the usage of synchronization primitives was simplified by a reduction of method calls for initialization.

One ongoing problem is the unification of API calls for dynamically and statically allocated resources. At the moment, the API predominantly supports dynamic allocation only. One solution would be to move the allocation outside the kernel, like the thread stack, which has to be allocated and then passed to the kernel. This approach allows for the same methods to be used regardless of the allocation strategy. However, a problem are the management structures that are placed inside the kernel. For processes, a macro creates these structures and registers them in the kernel on initialization. Although this works, macros tend to break Rusts programming workflow because they do not allow for software interfaces.

The API could be improved by adding constraints on kernel calls depending on the context. For example, in an interrupt, we are not allowed to call a blocking lock method on a mutex. This mistake is caught at runtime and we receive an error. However, if there were traits for different contexts, the interrupt handler could be constrained to valid API calls. Again that would move another error source from runtime to compile time.

Decisions on how allocation should be handled will have the biggest impact on the API stabilization. Other adjustments, such as the constraints for contexts, should not impact the user. Similarly, thanks to the builder pattern, new parameters can be added without breaking the API.

Internals

The internals of the kernel (e.g. scheduler) are not noticeable to the user until something goes wrong. If there is an error in the context switch, the system crashed immediately. Thus, for a stable application, the kernel internals must be stable and well tested. For that reason testing should cover more scenarios than it currently does. Besides testing, the linter clippy [46] should be introduced to the project to improve code quality and reduce software defects.

Toolchain

A requirement from the beginning was that Bern RTOS should compile on Rusts stable toolchain. It is expected that the target audience for the RTOS will only accept the stable toolchain. This requirement was relaxed to some degree during development. Nightly features such as inline assembly shortened iteration cycles compared to externally compiled assembly files. Over time, the inline assembly feature has been stabilized. Other nightly features were added only if there was progress on the stabilization, or if they can be replaced without much effort.

At the moment, only two nightly features are required. naked_functions is used for inline assembly functions where the compiler should not stack registers i.e. context switch. This feature can be replaced with global inline assembly functions. Additionally, Bern RTOS requires the user to include the alloc crate. The allocator itself needs an error function (default_alloc_error_handler). In the future, the kernel should not allow for purely static memory allocation, making the alloc crate optional.