Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add initial version of hypervisor tooling #18

Merged
merged 2 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion ebcl/common/deb.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,12 @@ def extract(self, location: Optional[str] = None,
return None

# find data.tar
tar_files = list(Path(deb_content_location).glob('data.tar.*'))
data_tar = Path(deb_content_location) / 'data.tar'
if data_tar.exists():
tar_files = [data_tar]
else: # find compressed data.tar
tar_files = list(Path(deb_content_location).glob('data.tar.*'))

if not tar_files:
logging.error('No tar content found in package %s!', self)
return None
Expand Down
Empty file.
115 changes: 115 additions & 0 deletions ebcl/tools/hypervisor/config_gen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import argparse
import logging
import os
from pathlib import Path

import jinja2
import yaml

from ebcl.common import init_logging, log_exception
from ebcl.common.files import resolve_file

from .schema_loader import BaseModel, FileReadProtocol, Schema, merge_dict


class BaseResolver:
"""
Resolve bases defined in the yaml config.
"""

def load(self, config_file: str, conf_dir: str) -> dict:
"""
Load config_file and all of its bases.
"""
config = {
"base": [config_file]
}
while config["base"]:
base_name = config["base"].pop(0)
base_path = resolve_file(
file=base_name, relative_base_dir=conf_dir)
old = config
config = self._load_file(base_path)
merge_dict(config, old)
return config

def _load_file(self, filename: str) -> dict:
with open(filename, "r", encoding="utf-8") as f:
return yaml.load(f, yaml.Loader)


class HvFileGenerator:
"""
Hypervisor configuration file generator
"""
config: BaseModel
schema: Schema
output_path: Path

def __init__(self, file: str, output_path: str, specialization: str | None = None) -> None:
""" Parse the yaml config file.
Args:
config_file (Path): Path to the yaml config file.
"""
self.output_path = Path(output_path)

""" Load yaml configuration. """
config_file = Path(file)

config = BaseResolver().load(config_file.name, str(config_file.parent))
del config["base"]

self.schema = Schema(specialization and Path(specialization) or None)
self.config = self.schema.parse_config(config)

def _render_template(self, outpath: Path, template: FileReadProtocol) -> None:
"""Render a template to target"""
template_obj = jinja2.Template(template.read_text("utf-8"), trim_blocks=True)

with outpath.open("w", encoding="utf-8") as f:
f.write(template_obj.render(config=self.config))

def create_files(self) -> None:
"""
Create three Files of HV
init.ned, io.cfg and modules.list
"""
self.output_path.mkdir(exist_ok=True)

for template in self.schema.templates:
base, ext = os.path.splitext(template.name)
if ext == ".j2":
logging.info("Rendering %s", base)
outpath = self.output_path / base
self._render_template(outpath, template)
else:
logging.info("Creating %s", template)
outpath = self.output_path / template.name
outpath.write_text(template.read_text("utf-8"))


@log_exception(call_exit=True)
def main() -> None:
""" Main entrypoint of EBcL hypervisor generator. """
init_logging()

logging.info('\n===================\n'
'EBcL Hypervisor Config File Generator\n'
'===================\n')

parser = argparse.ArgumentParser(
description='Create the config files for the hypervisor')
parser.add_argument('-s', '--specialization', type=str,
help='Path to hypervisor specialization directory')
parser.add_argument('config_file', type=str,
help='Path to the YAML configuration file')
parser.add_argument('output', type=str,
help='Path to the output directory')
args = parser.parse_args()

generator = HvFileGenerator(args.config_file, args.output, args.specialization)
generator.create_files()


if __name__ == "__main__":
main()
32 changes: 32 additions & 0 deletions ebcl/tools/hypervisor/data/io.cfg.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
local Res = Io.Res
local Hw = Io.Hw

local hw = Io.system_bus();

Io.Dt.add_children(hw, function()
{% for vbus in config.vbus %}
{% for dev in vbus.devices %}
{{dev.name}} = Hw.Device(function()
{% if dev.compatible %}
compatible = {"{{dev.compatible}}"}
{% endif %}
{%for mmio in dev.mmios %}
Resource.reg{{loop.index0}} = Res.mmio({{"0x%x"|format(mmio.address)}}, {{"0x%x"|format(mmio.address + mmio.size - 1)}}{% if mmio.cached %}, Io.Resource.F_cached_mem | Io.Resource.F_prefetchable{% endif %})
{% endfor %}
{%for irq in dev.irqs %}
Resource.irq{{loop.index0}} = Res.irq({{irq.irq + irq.offset}}, Io.Resource.Irq_type_{{irq.is_edge and "raising_edge" or "level_high"}})
{% endfor %}
end)
{% endfor %}
{% endfor %}
end)

Io.add_vbusses({
{% for vbus in config.vbus %}
{{vbus.name}} = Io.Vi.System_bus(function()
{% for dev in vbus.devices %}
{{dev.name}} = wrap(hw.{{dev.name}})
{% endfor %}
end),
{% endfor %}
})
45 changes: 45 additions & 0 deletions ebcl/tools/hypervisor/data/model.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://linux.elektrobit.com.com/model.schema.json",
"title": "Hypervisor Configuration Model",
"description": "Hypervisor configuration model",
"type": "object",
"additionalProperties": false,
"default": {},
"properties": {
"version": {
"type": "integer"
},
"classes": {
"type": "object",
"default": {},
"additionalProperties": {
"type": "object",
"additionalProperties": {
"type": "object",
"additionalProperties": false,
"properties": {
"type": {"type": "string"},
"default": {"type": ["string", "number", "boolean", "array"]},
"aggregate": {
"enum": ["None", "list"],
"default": "None"
},
"optional": {"type": "boolean", "default": false},
"enum_values": {"type": "array", "items": {"type": "string"}}
}
}
}
},
"root": {
"type": "string",
"optional": false
},
"templates": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
26 changes: 26 additions & 0 deletions ebcl/tools/hypervisor/data/modules.list.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
default-kernel fiasco -serial_esc

entry hv-image
roottask moe rom/init.ned

module l4re
module ned
module cons
module io
{% if config.vms %}
module uvmm
{% endif %}
{% if config.vio_block %}
module virtio-block
{% endif %}
{% if config.vnets %}
module l4vio_net_p2p
{% endif %}

module init.ned
module io.cfg
module system.lua

{% for module in config.modules %}
module {{module}}
{% endfor %}
134 changes: 134 additions & 0 deletions ebcl/tools/hypervisor/data/schema.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# yaml-language-server: $schema=model.schema.json

version: 1

classes:
HVConfig:
vbus:
type: VBus
aggregate: list
default: []
cons:
type: Cons
optional: true
shms:
type: SHM
aggregate: list
default: []
vms:
type: VM
aggregate: list
default: []

VM:
name:
type: string
kernel:
type: string
ram:
type: integer
cpus:
type: integer
cmdline:
type: string
default: ""
initrd:
type: string
optional: true
dtb:
type: string
vbus:
type: string
optional: true
vmnets:
type: string
aggregate: list
default: []
shms:
type: string
aggregate: list
default: []
virtio_block:
type: VirtioBlockNode
optional: true
vnets:
type: string
aggregate: list
default: []


VirtioBlockNode:
servers:
type: string
aggregate: list
default: []

clients:
type: string
aggregate: list
default: []

SHM:
name:
type: string
size:
type: integer
address:
type: integer
optional: true

Cons:
default_vm:
type: string
optional: true

VBus:
name:
type: string
devices:
type: Device
aggregate: list

Device:
name:
type: string
compatible:
type: string
optional: true
mmios:
type: MMIO
aggregate: list
optional: true
irqs:
type: IRQ
aggregate: list
optional: true
MMIO:
address:
type: integer
size:
type: integer
cached:
type: boolean
default: false
IRQ:
irq:
type: integer
type:
type: enum
enum_values:
- SGI
- PPI
- SPI
default: SPI
trigger:
type: enum
enum_values:
- level_high
- rising_edge

root: HVConfig

templates:
- io.cfg.j2
- system.lua
Loading