2024-12-20 11:57:22 +00:00
|
|
|
# Miou & Solo5
|
|
|
|
|
|
|
|
This library can be used to create unikernels with [Miou][miou] and
|
|
|
|
[Solo5][solo5]. The library exposes what Solo5 can expose and implements a
|
|
|
|
simple scheduler using Miou in order to develop a unikernel. Examples are
|
|
|
|
available in the [tests][tests] to show how to implement and build an
|
|
|
|
unikernel.
|
|
|
|
|
|
|
|
Basically, a unikernel can be built from 2 devices:
|
|
|
|
- the net device (a [TAP interface][tap])
|
|
|
|
- the device block (a simple file)
|
|
|
|
|
|
|
|
## How to build an unikernel with Miou & Solo5
|
|
|
|
|
|
|
|
Here's an example of a simple unikernel with `Miou_solo5` which displays
|
|
|
|
"Hello World":
|
|
|
|
```ocaml
|
|
|
|
let () = Miou_solo5.(run []) @@ fun () ->
|
|
|
|
print_endline "Hello World"
|
|
|
|
```
|
|
|
|
|
|
|
|
First of all, we can build a simple executable from this code:
|
|
|
|
```dune
|
|
|
|
(executable
|
|
|
|
(name main)
|
|
|
|
(modules main)
|
|
|
|
(modes native)
|
|
|
|
(link_flags :standard -cclib "-z solo5-abi=hvt")
|
|
|
|
(libraries miou-solo5)
|
|
|
|
(foreign_stubs
|
|
|
|
(language c)
|
|
|
|
(names manifest.sleep)))
|
|
|
|
```
|
|
|
|
|
|
|
|
As you can see, the executable needs a "manifest.c". This is a file that
|
|
|
|
describes the devices that the unikernel needs. In this case, the `run`
|
|
|
|
function takes a list of devices as its first argument, and we have just
|
|
|
|
specified no devices in our example.
|
|
|
|
|
|
|
|
As far as our _host toolchain_ is concerned, we can generate an empty file for
|
|
|
|
the "manifest.c" required:
|
|
|
|
```dune
|
|
|
|
(rule
|
|
|
|
(targets manifest.c)
|
|
|
|
(enabled_if
|
|
|
|
(= %{context_name} "default"))
|
|
|
|
(action
|
|
|
|
(write-file manifest.c "")))
|
|
|
|
```
|
|
|
|
|
|
|
|
Our first objective is to _infer_ the devices needed for our unikernel. In this
|
|
|
|
case, our unikernel in our host context is not really going to run. It will
|
|
|
|
collect the devices we have in the list we pass to `Miou_solo5.run` and generate
|
|
|
|
a JSON file describing the devices needed by our unikernel.
|
|
|
|
```shell
|
|
|
|
$ dune exec main.exe
|
|
|
|
{"type":"solo5.manifest","version":1,"devices":[]}
|
|
|
|
```
|
|
|
|
|
|
|
|
We can now specify a new toolchain, that of solo5 (available via the
|
|
|
|
[ocaml-solo5][ocaml-solo5] package) so that we can compile our OCaml code and
|
|
|
|
build our unikernel.
|
|
|
|
```shell
|
|
|
|
$ cat >dune-workspace <<EOF
|
|
|
|
(lang dune 3.0)
|
|
|
|
(context (default))
|
|
|
|
(context (default
|
|
|
|
(name solo5)
|
|
|
|
(host default)
|
|
|
|
(toolchain solo5)
|
|
|
|
(merlin)
|
|
|
|
(disable_dynamically_linked_foreign_archives true)))
|
|
|
|
EOF
|
|
|
|
```
|
|
|
|
|
|
|
|
However, this time the "manifest.c" file must not be empty. It will be the
|
|
|
|
result of a tool, `solo5-elftool` (available via the `solo5` package), which
|
|
|
|
generates the "manifest.c" file from a "manifest.json" file describing the
|
|
|
|
devices needed for our unikernel.
|
|
|
|
|
|
|
|
We are going to describe two new rules. The first will generate our
|
|
|
|
"manifest.json" from our unikernel compiled in the host context, and the second
|
|
|
|
will use this "manifest.json" to generate the "manifest.c" file in the Solo5
|
|
|
|
context:
|
|
|
|
```dune
|
|
|
|
(rule
|
|
|
|
(targets manifest.c)
|
|
|
|
(deps manifest.json)
|
|
|
|
(enabled_if
|
|
|
|
(= %{context_name} "solo5"))
|
|
|
|
(action
|
|
|
|
(run solo5-elftool gen-manifest manifest.json manifest.c)))
|
|
|
|
|
|
|
|
(rule
|
|
|
|
(targets manifest.json)
|
|
|
|
(enabled_if
|
|
|
|
(= %{context_name} "solo5"))
|
|
|
|
(action
|
|
|
|
(with-stdout-to
|
|
|
|
manifest.json
|
|
|
|
(run %{exe:main.exe}))))
|
|
|
|
```
|
|
|
|
|
|
|
|
We can now compile our unikernel with a simple: `dune build`!
|
|
|
|
```shell
|
|
|
|
$ dune build
|
|
|
|
$ solo5-hvt _build/solo5/main.exe --solo5:quiet
|
|
|
|
Hello World
|
|
|
|
```
|
|
|
|
|
|
|
|
The executable `_build/solo5/main.exe` is not really an executable but an OS!
|
|
|
|
What's more, you can't run it as a simple program but "virtualise" it using the
|
|
|
|
_tender_ `solo5-hvt`. Congratulations, you've made a _complete_ operating
|
|
|
|
system in OCaml!
|
2024-12-20 12:45:41 +00:00
|
|
|
|
|
|
|
[miou]: https://github.com/robur-coop/miou
|
|
|
|
[solo5]: https://github.com/Solo5/solo5
|
|
|
|
[tap]: https://en.wikipedia.org/wiki/TUN/TAP
|
|
|
|
[ocaml-solo5]: https://github.com/mirage/ocaml-solo5
|