MirageOS - parametric compilation depending on target

I’ve been looking at a couple of libraries, but I was unable to find any examples of a config.ml that matches on mirage configure --target (xen/ukvm/unix) and links appropriate implementations and applies the functors.

There seems to be some built-in voodoo for this for type Mirage.http etc, but how are “normal” developers supposed to express it?

I would be very grateful if someone could point me in the direction of a solution to this.

You should use the if_impl and match_impl combinators, as described in the blog post introducing functoria. For example, here is the implementation of the console device:

let custom_console str =
  match_impl Key.(value target) [
    `Xen, console_xen str;
    `Qubes, console_xen str;
    `Virtio, console_solo5 str;
    `Ukvm, console_solo5 str
  ] ~default:(console_unix str)
1 Like

@Drup thank you! I got that working now.

Next question: How do I obtain the values returned by the connect! method of impl objects?
It looks to me like they are currently ignored with >>= fun _ -> in my generated main.ml, but I can’t figure out how to capture them?

I’m not sure what you want to achieve, but the only way to change how device behaves is to write your own devices, like it is done in mirage.ml. You can use inheritance if necessary.

To give some context here, I believe @cfcs is trying to implement a framebuffer device. With -t qubes, Mirage will connect to the Qubes GUI (because Qubes requires that) but doesn’t expose this to the application or do anything else with it.

I think that -t qubes is currently only useful for programs that don’t know about Qubes but want to run on it without changes. Programs that actually want to interact with Qubes-specific services should not use -t qubes (but should use -t xen instead) currently.

The problem is that you might want to have Mirage connect to “all the default Qubes services, except for the framebuffer, which I want to handle explicitly”.

Can anyone think of a cleaner solution?

/cc @yomimono

The specific problem I’m trying to solve is that using the -t qubes target applies the connect method of impl's whose results are ignored:

let mirage1 = lazy (
  let __qrexec_1 = Lazy.force qrexec_1 in
  let __gui1 = Lazy.force gui1 in
  let __key1 = Lazy.force key1 in
  let __f11 = Lazy.force f11 in
  __qrexec_1 >>= fun _qrexec_1 ->
  __gui1 >>= fun _gui1 ->
  __key1 >>= fun _key1 ->
  __f11 >>= fun _f11 ->
  Lwt.return_unit
  )

I would like to capture the _qrexec_1, _gui1 values (they contain handles required for use with the Qubes [mirage-qubes] ibrary); otherwise the qubes target would appear a bit useless for applications that actually want to interact with Qubes.
I can do the initialization manually with the xen target, but I feel like I’m missing something.

Unfortunately you’re not, in the current state of the world. qubes-mirage-firewall is in the same situation, IIRC. I failed to think of a nice way to handle this when setting up -t qubes but that’s definitely not to say there isn’t one.

Right, I got confused because I didn’t follow the details of the qubes target very closely.

@cfcs I think the way forward for you right now would be to patch mirage.ml.

One possibility, without studying the issue in details, would be introduce something similar to the argv device: One or several devices specific for qubes that exposes all the new ressources and the mirage device would conditionally depend on them.

Alright everyone, thanks for the wisdom, this makes sense!

I’ll just go with the Xen target for now, as I don’t anticipate any other backends that work on Xen any time soon (the vesa/VGA target could be relevant in the future, but oh well).

Sorry to bump this, but just wanted to leave a link to my solution for future reference.

I couldn’t figure out how to unify impls that had method ty = qubesdb @-> framebuffer and method ty = framebuffer respectively, so I ended up packing the applied functor and returning that.
I am still hoping that someone more experienced with MirageOS will come up with a better solution.

1 Like

Well done! I’m always happy to see constructive proofs that people other than the mirage devs can write devices. :stuck_out_tongue:

What do you mean by “unify” here ? It seems to me like you are applying almost all your functors by hand, which is more or less what should be avoided.

Well done! I’m always happy to see constructive proofs that people other than the mirage devs can write devices. :stuck_out_tongue:

Thank you! :slight_smile:

What do you mean by “unify” here ? It seems to me like you are applying almost all your functors by hand, which is more or less what should be avoided.

For background information: The .t types in the Qubes modules are
used to store vchan handles
for communication with the operating system.

  • Functor applications: The problem is that I had to capture the
    Qubes.GUI.t handle in order to call
    Framebuffer(Framebuffer_qubes).init, and return that (as mentioned
    above).

This means I had to use the mirage configure -t xen target to avoid
the initialization of the qubesdb impl, and do the initialization
myself (arguably dirty, but not sure how to avoid that otherwise).

To avoid having to expose the Qubes.GUI.t type in the applied
Framebuffer functor I opted to initialize it and return a packed
module

I didn’t see any other obvious way to do it, but I’m open to other ideas :slight_smile:

  • Unification: My original idea was that I could somehow have one impl
    that depended on qubesdb so that I could receive the initialization
    handle.

EDIT: My post got cut off for some reason.

  • Having a target-specific store for global state (such as these handles), as you proposed above, would be much nicer. Especially if it was available to other impl’s.
    Unfortunately I have no idea how to implement this. :frowning:

  • Alternatively we could go for a more specific solution to the problem and store the initialized “state handles” in mutable fields in the QubesDB module, and expose mock implementations for targets that do not actually work with Qubes [instead of halting the compilation as the device currently does]. (@yomimono, @talex5, what do you think about this idea?)