From 72d23be3075cd5c5102320d19482833d4e98d352 Mon Sep 17 00:00:00 2001 From: Joerg Vehlow Date: Thu, 13 Feb 2025 08:50:02 +0000 Subject: [PATCH] hypervisor: initial documentation --- docs/SUMMARY.md | 1 + docs/assets/hv_overview.drawio | 97 ++++++++++++ docs/assets/hv_overview.png | Bin 0 -> 70360 bytes docs/hypervisor_config.md | 271 ++++++++++++++++++++++++++++++++ ebcl_build_tools.code-workspace | 3 +- 5 files changed, 371 insertions(+), 1 deletion(-) create mode 100644 docs/assets/hv_overview.drawio create mode 100644 docs/assets/hv_overview.png create mode 100644 docs/hypervisor_config.md diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 1bdb902..10eaaa4 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -7,5 +7,6 @@ - [Initrd Generator](initrd_generator.md) - [Boot Generator](boot_generator.md) - [Downloader](downloader.md) +- [Hypervisor Config Generator](hypervisor_config.md) - [Proxy](proxy.md) - [Common](common.md) diff --git a/docs/assets/hv_overview.drawio b/docs/assets/hv_overview.drawio new file mode 100644 index 0000000..e7ad37a --- /dev/null +++ b/docs/assets/hv_overview.drawio @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/assets/hv_overview.png b/docs/assets/hv_overview.png new file mode 100644 index 0000000000000000000000000000000000000000..7ed24d0944975f78adc6b244392047f3ee622a35 GIT binary patch literal 70360 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®OMkrj>aQas>kCt{!rNgSnL)r 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 literal 0 HcmV?d00001 diff --git a/docs/hypervisor_config.md b/docs/hypervisor_config.md new file mode 100644 index 0000000..ef1eda4 --- /dev/null +++ b/docs/hypervisor_config.md @@ -0,0 +1,271 @@ +# Hypervisor Config Generator +The hypervisor config generator can create configuration files for the Elektrobit Hypervisor from a declarative configuration in yaml configuration files. + +## Background +Thy Elektrobit hypervisor traditionally is configured using the lua interface. +Configuration of the hypervisor usually consists of: + + * Virtual Machines (e.g. linux) to be started with assigned resources like + * CPUs + * RAM + * Passthrough hardware devices + * ... + * Applications running directly on the hypervisor + * Available physical hardware + * Virtual hardware like network interface pairs + +The configuration using lua is very flexible, but being a full-fledged imperative programming language it is not as straight forward as it could be. + +For that reason this tool was created that can turn a declarative definition of the system configuration into the required lua code. + + +Note: In the future the safety relevant applications of the hypervisor will require configuration in a format different from lua (e.g. compiled binary or binary configuration files). These files have to be created/generated as well which will likely be a part of this tool in the future. + +## Overview +![Elektrobit](assets/hv_overview.png) + +The image above shows the main components of the hypervisor config generator and the surrounding elements. +It shows the tooling itself (*hypervisor config tool*), which is the python code and some data (*builtin mode, schema and template*) that is described later. +There is also the *declarative configuration* that is supplied by the use (i.e. it is part of the image description) and the resulting *lua configuration* that is generated by the tool and should be built into the hypervisor image by the surrounding toolchain. +The last part shown here is the optional *hypervisor specialization*. It can be used to extend or change the model used for the configuration and also replace or extend the configuration files generated. + +### Model and Schema +As described in the overview chapter the tool comes with a model and schema definition, that can be extended using a hypervisor specialization. The main reason for that mechanism is that the hypervisor can provide different features depending on the build (i.e. a hypervisor with and without safety extensions). +These features must be configurable and at the time of writing this tool and documentation it does not seem to be viable to integrate all possible features into the tool itself, so it is "outsourced" to a package created together with the hypervisor binaries. + +In this context the *schema* is a yaml file that describes all possible properties in the configuration file with their datatypes. It can be compared to [JSON Schema](https://json-schema.org/) but is very specific to the hypervisor and limited to +required features. Everything in the configuration file is either a simple datatype (like string, integer or boolean) an object of a specific type (i.e. a named collection of specified properties) or a list of these. +This schema is then used to parse the configuration file into a python object *model*. The model can also be extended/overwritten by the hypervisor specialization. For every named object type defined in the schema a python class exists or is dynamically created. This allows to add some functionality to transform the configuration or derive information using python code. + +The schema also defined the templates that are rendered into the final lua configuration using the python model in the last step of the tool. The templates can either be jinja2 templates (ending in .j2) then they are processed or they are just copied (e.g. for code libraries). + + +## Usage +The tool always requires the configuration yaml file and the output directory: +```bash +hypervisor_config config.yaml output_dir +``` + +When a hypervisor specialization is to be used there are two variants: + +1. Pass a directory containing the specialization (schema.yaml and/or model.py and templates) + `hypervisor_config --specialization specialization_dir config.yaml output_dir` +2. Pass a debian package name and a config file that contains repository definitions in the format of the other build tools: + `hypervisor_config --specialization-package package_name --repo-config repo_config.yaml config.yaml output_dir`. + This will automatically download the package and use the contained specialization (by searching for a directory with schema.yaml). + +The generated config files can then be used to rebuild the hypervisor binary using the l4image tool. + +## Reference +### Schema +This chapter describes the format of the schema.yaml file. +As written above the schema file describes the hypervisor configuration file. + +There are four top level elements in the schema: + + * *version*: At the moment this is always `1`. It will be used when breaking changes are required. + * *classes*: This describes the available object types of the model + * *root*: The type of the root node of the model + * *templates*: A list of relative paths to templates that are rendered ().j2) or copied to the final configuration folder. + +#### Available datatypes +All configuration elements are typed. +There are some datatypes for basic types built into the parser. +These are: + + * *string*: A simple string + * *integer*: A natural positive or negative number + * *boolean*: A `True` or `False` value (in yaml format `true` or `false`) + +Additional datatypes can be defined using the classes collection in the schema. + +#### The *classes* element +The classes element describes a collection of classes used to parse the configuration into. +The collection is a yaml mapping where the key is the name of the class and the value is a mapping of properties to their type. + +```yaml +classes: + VM: + name: + type: string + kernel: + type: string + ram: + type: integer +``` +In this example a class `VM` is defined with three properties (`name`, `kernel` and `ram`). +When this type is used and loaded all three values MUST be specified. + +##### Properties of classes +In the previous chapter a class is described as a mapping of properties to their type. +A type is also a mapping to allow for more complex type definitions than just scalars. +The following properties are valid in a type definition: + + * *type*: The scalar datatype, a name of a class in the classes collection or `enum`. + * *optional*: Defaults to `false`. If set to `true` the value may be omitted in the configuration and will be set to the default value. + * *default*: Defaults to `None`. Can be set to a default value when optional is `true`. + * *enum_values*: If *type* is set to `enum` a sequence of strings of valid values for the enum. An enum is represented as a string in the python model. + * *aggregate*: Currently this supports only `list`. If set the parser expects a sequence of objects of the type defined in `type`. This allows for example to define a list of virtual machines.\ + Even for list aggregates a default value can be used. For example for an empty list `[]` can be used. It would also be possible to define one or more objects in the default. + +#### The *root* element +The *root* element describes the type expected for the whole configuration file. +This type should be a container for all elements required to configure the hypervisor. +In the builtin schema it is `HVConfig` which is a container that holds virtual machine, device configuration and others. + +#### Example + +```yaml +version: 1 + +classes: + # Define a class called Root + Root: + # A simple integer + simple_int: + type: integer + # An optional string that will have the value "default_value" when not specified + optional_str: + type: string + optional: true + default: default_value + # A list of boolean that will have the default value [true, false, false] when not specified + list_of_bool: + type: boolean + aggregate: list + optional: true + default: + - true + - false + - false + # An enum with valid values: value_a or value_b + an_enum: + type: enum + enum_values: + - value_a + - value_b + # An optional list of objects from type AnotherObject. Will be None if not specified + list_of_objects: + type: AnotherObject + aggregate: list + optional: true + + # Define a class called AnotherObject ... + AnotherObject: + # ... with just one property name as string + name: + type: string + +# The root class is the root node +root: Root + +# Define templates to be rendered +template: + - jinja_template.cfg.j2 + - copy_me.lua +``` + +The schema above can parse this configuration file: +```yaml +simple_int: 42 +optional_str: some string +list_of_bool: + - false + - false + - true +an_enum: value_b +list_of_objects: + - name: object 1 + - name: object 2 +``` + +Parsing this will return an instance of a `Root`-object with the five properties set and the `list_of_objects` property will contain a list of two `AnotherObject` instances. +This root object is passed to the jinja templates with the name `config` and can then be used to render the templates. + +Two templates are rendered in this example: *jinja_template.cfg.j2* is rendered using jinja2 and will be stored in the output folder as *jinja_template.cfg*. While *copy_me.lua* is just copied to the output folder. + + +### Model +In this context model means the python object model representing the configuration. +It is used to further process and validate the configuration to simplify the process of writing configuration templates in jinja. +One example for processing done in the model is collecting all kernel and device trees into a single list for inclusion into the modules.list that is then used to created the hypervisor binary. + +All classes defined in the schema may have a defined python class with the same name. +This is not necessary though, if a class does not exist it is dynamically created. +All model classes must be derived from `BaseModel` which is used for parsing the configuration. + + +### Hypervisor Specialization +The hypervisor specialization is used to enhance the schema, model and templates used to generate the configuration. +It allows customization depending on the supported features available in the hypervisor binary. +A specialization is a directory containing one or all of a `schema.yaml`, `model.py` and templates. + + +#### Schema specialization +The specialization schema is loaded after the builtin schema and can extend it. +It can add new properties to existing classes, add new classes, change the root class and add/overwrite templates. + + +##### Example +The example below shows a snipped from the builtin schema and the specialization schema that adds a new property `hi` to the `VM` class and a new template. +This new property can then be used in the template to generate code that is only required if the virtual machine is a high integrity VM. + +Builtin schema: +```yaml +... +classes: + VM: + name: + type: string +... +``` + +Specialization schema: +```yaml +version: 1 + +classes: + VM: + hi: + type: boolean + default: false + optional: true + +templates: + - init.ned.j2 +``` + +#### Model specialization +When a specialization contains a model.py it is loaded after the builtin module and can overwrite existing classes or add new classes. +This can be used to add properties to the python classes or even add new processing of the configuration. +It is also possible to extend the existing class by inheritance. + +##### Example +This example shows how a validation step can be integrated for the schema extension defined below. +In this hypothetical example a high integrity VM always requires the shared memory segments named `fbshm`, `proxycomshm`, `hicomshm`, `tmshm` and `wdgshm`. + + +```py +from ebcl.tools.hypervisor.model_gen import ConfigError +from ebcl.tools.hypervisor.model import VM as VMBase + +class VM(VMBase): + HI_EXPECTED_SHMS = set(( + "fbshm", + "proxycomshm", + "hicomshm", + "tmshm", + "wdgshm" + )) + hi: bool + """True if this is a high integrity VM""" + + def finalize(self, registry: HVConfig) -> None: + super().finalize(registry) + shm_names = [shm.name for shm in self.shms] + missing = self.HI_EXPECTED_SHMS.difference(shm_names) + if missing: + raise ConfigError(f"For a hi VM the following shms are missing: {', '.join(missing)}") +``` +The specialization model derives the class `VM` from the builtin `VM` class and adds a validation step to the `finalize` method. +Note that the `finalize` method is not (yet) called for all classes defined in the model. diff --git a/ebcl_build_tools.code-workspace b/ebcl_build_tools.code-workspace index 0018498..347b907 100644 --- a/ebcl_build_tools.code-workspace +++ b/ebcl_build_tools.code-workspace @@ -8,7 +8,8 @@ "cSpell.words": [ "debootstrap", "ebcl", - "minbase" + "minbase", + "Elektrobit" ] } }