RFC - Basic Configuration
This RFC proposes a basic configuration system for use in the kernel.
The goal of this system is to allow for a reduction in target-specific code, including potentially removing separate projects for similar targets, such as the lichee-rv, mq-pro, and beepy projects.
This system aims to address current problems, and get us to the next "order of magnitude" of problems (by ignoring many problems we know will be a "later problem").
In Broad Strokes
The config
crate
We would add a new config
crate that provides helpers for loading configuration at compile time from a json
or toml
format.
This crate would also provide helpers for "baking" this data at compile time using the databake crate in a build.rs
file, and compiling into the target as code.
The config
crate would also provide a MnemosConfig
type that is generic over two types:
- A
KernelConfig
type, which contains all kernel-defined configuration, provided by thekernel
crate - A
PlatformConfig
type, which contains all target- or family-specific configuration, expected to be provided by any of the definedplatforms
that mnemos supports
This crate uses the generics to avoid circular dependencies between the config crate and kernel/platform crates. It is intended that "user facing" applications of the config crate (primarily in platform crates) is done by providing type aliases to "hide" the generics.
Kernel changes
The kernel will provide a single KernelConfig
type, which is a struct that contains all service and daemon configuration values. We will add functionality to the kernel to take a provided KernelConfig
type, and automatically launch the configured services.
This capability will allow most platform targets to avoid repeated boilerplate kernel.initialize(...)
calls at startup.
Platform crate changes
Platform crates will be expected to provide a PlatformConfig
type, containing all drivers or daemons that are specific or applicable to this platform (other than the ones defined in the kernel itself). Similar to the kernel, they should also provide a single call that takes a KernelConfig
and PlatformConfig
, and automatically launches all kernel and platform services.
One platform may have many targets: For example, the Allwinner D1 platform has three targets currently: the lichee-rv
dev board, the mq-pro
dev board, and the beepy
.
With this system in place, each of these targets would no longer have specific binary targets, and would instead share a single binary target with different configuration files, loaded at compile time.
This configuration file could be set via an environment variable such as MNEMOS_CONFIG=/path/to/beepy.toml
, which would be loaded by a build.rs
script, and set by just
.
In this way, customized targets can be made by creating a new configuration file, rather than an entire cargo project.
Compile Time vs Runtime Configuration
At the moment, most configuration will be done at compile time, with configuration values being "baked into" the binary.
However, we should ensure that all configuration values also implement serde
traits, so that platforms that CAN load configuration at runtime (from an SD Card or other external sources) are able to.
Platforms that can load configuration at runtime may need to provide a "two stage" initialization process: using enough of the "baked in" configuration to initialize the SD Card or other external source, then continuing with the initialization process after merging or replacing the baked in data with the loaded data.
Inspiration
This approach is largely inspired by ICU4X's Data Provider model, which aims to "bake in" some common assets, but also allow for loading additional assets upon request.
Other potential inspirations
- https://crates.io/crates/config
- https://github.com/rust-osdev/bootloader#usage
"Tomorrows Problems"
This initial approach avoids many complexities by ignoring them completely. Some of these complexities are described below:
Compile time feature enablement
At some point, we may have many drivers, some of which that are not relevant or possible to support on some platforms. Since this system will support runtime configuration, the compiler will still need to compile them into the kernel image, even though there is no chance of them being used.
This approach does not address compile time gating of features to prevent certain capabilities from being compiled in at all.
While this may lead to some "code bloat", we are not at a stage where this is an immediate concern. Once we are, our configuration approach may need to be revised.
Extensibility
Similarly as new features are added that COULD be supported on all platforms, we will need to add them manually to the configuration items of each crate.
For example: if we have SPI drivers in many platforms, and write a bunch of drivers for external components connected over SPI, we will need to add configuration for each driver to all platforms.
This might be mitigated by grouping these into a common "all spi drivers" configuration type that is included "by composition" into all platform crates, but at some point this will cause friction and/or maintenance burden.
We may want to revisit how to support that in the future.
Layering of config
Eventually we will probably want some kind of way to "layer" configs, for example providing a base level of defaults, and adding partial overlays when adding components.
"demand based" configuration
This configuration approach makes no attempt at addressing validation of configuration, particularly looking at the dependencies of each service to determine if the configuration is suitable, nor detecting whether the configured hardware actually exists.
For example, a platform may configure the sermux service, but forget to provide a serial port for the sermux service to use, or a platform might configure a wifi adapter that isn't present on the target board.
At the moment, this RFC just says "don't do that", or "catch that in testing", which will not scale past a certain complexity level
Generating or building a platform config
This RFC makes no suggestion on how platforms should generate a comprehensive config file. We will probably want this eventually.