This library can be used to create unikernels with Miou and 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)
- the device block (a simple file)
Here's an example of a simple unikernel with Miou_solo5
which displays
"Hello World":
let () = Miou_solo5.(run []) @@ fun () ->
print_endline "Hello World"
First of all, we can build a simple executable from this code:
(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:
(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.
$ 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 package) so that we can compile our OCaml code and build our unikernel.
$ 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:
(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
!
$ 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!