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

Xen support #387

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open

Xen support #387

wants to merge 8 commits into from

Conversation

CertainLach
Copy link

@CertainLach CertainLach commented Aug 25, 2024

NixOS/nixpkgs#324911 implements Xen hypervisor support in NixOS, this PR implements Xen support in lanzaboote when used together with mentioned PR.

  • It is impossible to have xen enabled for specialization, only primary system can have it, due to Extensions for specializationn DeterminateSystems/bootspec#147

  • Current implementation produces very large EFI binaries (>160M, prepare your /boot partitions), because Xen can't verify linux image by itself, and it needs to be included in Xen unified kernel image (https://xenbits.xenproject.org/docs/unstable/misc/efi.html), which only supports unpacked (vmlinux) kernels (as far as I can see, using any other format (vmlinuz/bzImage) results in "Not an ELF binary" error).

    • Looks like bzImage not working was caused by alignment issues, with 4K section alignment it just works, and EFI binary was shrunk to 40M
    • Maybe it is possible to create a stub linux kernel, which will load the real kernel from directory? xen.efi is still big, but I don't see a way to implement proper file separation for it. Unless... How hard is to assemble and execute unified xen image in-memory during the boot?
  • Because there is no path to vmlinux image present in boot.json, extra configuration is required:
    boot.bootspec.extensions."org.xenproject.bootspec.v1".vmlinux = "${config.system.build.kernel.dev}/vmlinux";

    • bzImage now just works.
  • Created xen boot entries are located lower than entries obtained from plain nixos UKI images, because I haven't figured how to provide systemd-boot with sort key.

    • Entry order is now correct
  • Garbage collection doesn't work for /boot/loader/entries (I'm not sure how it is supposed to work).

    • Fixed.

Cc: @SigmaSquadron

@SigmaSquadron
Copy link

This is wonderful; thank you!

Created xen boot entries are located lower than entries obtained from plain nixos UKI images, because I haven't figured how to provide systemd-boot with sort key.

I'm using the same sort_key in the systemd-boot builder. Due to the (with Xen Hypervisor) in parenthesis right next to the normal title, systemd-boot bumps it higher up in the list.

It is also impossible to have xen enabled for specialization, only primary system can have it, due to DeterminateSystems/bootspec#147

I asked Ryan Lahfa in the Bootspec room when I was figuring out the stuff needed for the systemd-boot builder, and they mentioned that you can look into the boot.json for each specialisation, and while it looks like a normal boot.json, it doesn't have the specialisation extension, and the top-level arguments are specific to that specialisation. I imagined booting Xen in specialisations would also work, as if the specialisation is meant to be a Xen dom0, the specialisation-specific boot.json would have Xen entries.

@CertainLach
Copy link
Author

I'm using the same sort_key in the systemd-boot builder. Due to the (with Xen Hypervisor) in parenthesis right next to the normal title, systemd-boot bumps it higher up in the list.

I don't have "with Xen Hypervisor" here, sort-key was not enough, I have added version & renamed entry file name (== entry id) to make it sort correctly.

My primary system has xen enabled, and I have specialization with noxen tag, and it now orders entries exactly as with plain UKI images.

@CertainLach
Copy link
Author

I asked Ryan Lahfa in the Bootspec room when I was figuring out the stuff needed for the systemd-boot builder, and they mentioned that you can look into the boot.json for each specialisation

Lanzaboote uses top-level boot.json, where specializations are described using "org.nixos.specialisation.v1" key.

It is possible to look directly in specialization directories (/run/current-system/specialisation/X/boot.json), but it looks wrong, toplevel definition (/run/current-system/boot.json) should work too.

@CertainLach
Copy link
Author

For reference, current bootctl list output, where primary system has xen enabled, and noxen specialization has xen disabled. The ordering in bootctl list matches one in bootloader.

         type: Boot Loader Specification Type #1 (.conf)
        title: NixOS Vicuna 24.11.20240825.dirty (Linux 6.6.47) (Generation 250, 2024-08-25) (default) (selected)
           id: nixos-generation-250-ic4qfdwopgpyk4vifr3lub4png6dgnydgwqxsi6pt5yosoczhdmq.efi.conf
       source: /boot//loader/entries/nixos-generation-250-ic4qfdwopgpyk4vifr3lub4png6dgnydgwqxsi6pt5yosoczhdmq.efi.conf
     sort-key: lanza
      version: Generation 250, 2024-08-25
          efi: /boot//EFI/Linux/nixos-generation-250-ic4qfdwopgpyk4vifr3lub4png6dgnydgwqxsi6pt5yosoczhdmq.efi

         type: Boot Loader Specification Type #2 (.efi)
        title: NixOS Vicuna noxen-24.11.20240825.dirty (Linux 6.6.47) (Generation 250-noxen, 2024-08-25)
           id: nixos-generation-250-specialisation-noxen-zmqh6yrzuiqnotjuh7dyn26vml6thymb2fdiz3dqoctwymr3d3jq.efi
       source: /boot//EFI/Linux/nixos-generation-250-specialisation-noxen-zmqh6yrzuiqnotjuh7dyn26vml6thymb2fdiz3dqoctwymr3d3jq.efi
     sort-key: lanza
      version: Generation 250-noxen, 2024-08-25
        linux: /boot//EFI/Linux/nixos-generation-250-specialisation-noxen-zmqh6yrzuiqnotjuh7dyn26vml6thymb2fdiz3dqoctwymr3d3jq.efi
      options: init=/nix/store/9a6z9lg6n7c8zhx8z4ycqf0sjni70wq3-nixos-system-ema-noxen-24.11.20240825.dirty/init mitigations=off nohz_full=1-15 msr.allow_writes=on root=fstab loglevel=4 nohibernate kvm-intel.vmentry_l1d_flush=never

         type: Boot Loader Specification Type #1 (.conf)
        title: NixOS Vicuna 24.11.20240825.dirty (Linux 6.6.47) (Generation 249, 2024-08-25)
           id: nixos-generation-249-3g3znwubcnp4qfv7pdel2ji574jrhmpzsz5i3huruvbi4cia3u5a.efi.conf
       source: /boot//loader/entries/nixos-generation-249-3g3znwubcnp4qfv7pdel2ji574jrhmpzsz5i3huruvbi4cia3u5a.efi.conf
     sort-key: lanza
      version: Generation 249, 2024-08-25
          efi: /boot//EFI/Linux/nixos-generation-249-3g3znwubcnp4qfv7pdel2ji574jrhmpzsz5i3huruvbi4cia3u5a.efi

         type: Boot Loader Specification Type #2 (.efi)
        title: NixOS Vicuna noxen-24.11.20240825.dirty (Linux 6.6.47) (Generation 249-noxen, 2024-08-25)
           id: nixos-generation-249-specialisation-noxen-w4g5yjkq7n4r5ylsbtk4urnbm7eku5aureihcisoajzwjwoh4akq.efi
       source: /boot//EFI/Linux/nixos-generation-249-specialisation-noxen-w4g5yjkq7n4r5ylsbtk4urnbm7eku5aureihcisoajzwjwoh4akq.efi
     sort-key: lanza
      version: Generation 249-noxen, 2024-08-25
        linux: /boot//EFI/Linux/nixos-generation-249-specialisation-noxen-w4g5yjkq7n4r5ylsbtk4urnbm7eku5aureihcisoajzwjwoh4akq.efi
      options: init=/nix/store/cv7nvw9a3wfzrpml3hvi5ys01gcria9j-nixos-system-ema-noxen-24.11.20240825.dirty/init mitigations=off nohz_full=1-15 msr.allow_writes=on root=fstab loglevel=4 nohibernate kvm-intel.vmentry_l1d_flush=never

@CertainLach
Copy link
Author

Marking as ready for review, as I don't plan to change too much implementation-wise here.
I have experimented with adding hash validation to xen.efi, making it possible to not embed bzImage/ramdisk inside of UXI, but this is far from being done, and will go to upstream xen first, right now it will consume 40MB of /boot storage per generation.

Only toplevel system having xen sucks, but I believe it may need to be resolved in some more generic way, maybe it would be better to have xen installed the same way as memtest/netboot.xyz, instead of intercepting nixos entry generation, and then the solution will be the same as for #273?
I also want to hear bootspec people feedback on DeterminateSystems/bootspec#147, and my proposed solution (Comment in the same issue).

Anyway, opening this PR for feedback on those topics, hoping some better solution might arise for those issues.

@CertainLach CertainLach marked this pull request as ready for review August 29, 2024 19:50
@SigmaSquadron
Copy link

I believe it may need to be resolved in some more generic way, maybe it would be better to have xen installed the same way as memtest/netboot.xyz, instead of intercepting nixos entry generation, and then the solution will be the same as for #273?

That was the original idea before the UKI shenanigans in NixOS/nixpkgs#324911. This isn't an ideal solution, as it's expected that end users be able to roll back to a previous generation while keeping their Xen domains running.

@CertainLach
Copy link
Author

CertainLach commented Aug 29, 2024

That was the original idea before the UKI shenanigans in NixOS/nixpkgs#324911. This isn't an ideal solution, as it's expected that end users be able to roll back to a previous generation while keeping their Xen domains running.

No, I didn't meant it like that, I meant some generic way of producing per-entry /boot/loader/entries/X files with corresponding signed EFIs in lanzaboote. Currently, only xen generations can produce Boot Loader Specification Type #1 (.conf) files in lanzaboote.

netboot.xyz/memtest don't need to be per-entry, but the solution might be generic enough to enable both.
I.e configurationLimit for non-nixos entries might allow to have 20 generations of nixos, having 5 generations of xen to reduce space consumption, and then 1 generation of memtest? Idk.

@CertainLach
Copy link
Author

CertainLach commented Aug 29, 2024

How it may look in bootspec:

// Defined in top-level boot.json, no-op if defined on specialization level.
"org.nix-community.lanzaboote.extra-entries": {
  // Enabling xen in nixos will make two bootloader entries - one with xen bootloader, one without for troubleshooting.
  // I don't think it will be good to make it easy to have user locked in xen with no recovery options.
  xen1: {
    // generator kind describes how to populate efi sections, various kinds will be defined in lanzaboote
    // by code, I don't think it makes sense to make that fully declarative.
    generator: {
      kind: "xen",
      // Null for top-level
      specialization: null,
    },
    efi: "/nix/store/blah-blah-1/xen.efi"
  },
  // Instead of assigning xen config to specialization, xen bootloader entry will specify specialization which it
  // should use.
  xen2: {
    generator: {
      kind: "xen",
      specialization: "specialization-name",
    },
    efi: "/nix/store/blah-blah-2/xen.efi"
  },
  memtest: {
    efi: "/nix/store/blah-blah-3/memtest.efi"
  }
}
boot.lanzaboote.configurationLimit = 20;
boot.lanzaboote.extraEntries.xen1 = {
  configurationLimit = 5;
  efi = "${pkgs.xen}/xen.efi";
  generator = {
    kind = "xen";
    specialization = "specialization-name";
  };
};
# Or, for compatibility with configuration implemented currently in nixos (pseudocode)
boot.lanzaboote.extraEntries = prefixAttrs "xen-" (filterMapAttrs config.specialisation
  (spec: spec.configuration.boot.xen.enable)
  (spec: {
    efi = "${spec.configuration.boot.xen.package}/xen.efi";
  })
);
boot.lanzaboote.extraEntries.memtest = {
  configurationLimit = 1;
  efi = "${pkgs.memtest}/memtest.efi";
};

@gador
Copy link

gador commented Oct 20, 2024

@CertainLach thanks for your work here! Is this a working example you posted? What would be needed to give this PR a test run with an existing lanzaboote config?

@CertainLach
Copy link
Author

@gador The thing I posted is for the better, future implementation of the same functionality.
Xen itself will just work if you use lanzaboote built from this PR, no changes to NixOS config are required.

@gador
Copy link

gador commented Oct 23, 2024

Awesome, thanks, Ill give it a try 👍

Makes it possible to have system specialisation without xen, but now,
only the toplevel system derivation can have xen installed, xen
extension is always ignored for specialisations.

Issue: DeterminateSystems/bootspec#147
@CertainLach
Copy link
Author

CertainLach commented Nov 11, 2024

Rebased & matched behavior with nixpkgs implementation - it is now produces two boot entries per generation if xen is enabled.

@CertainLach
Copy link
Author

CI failures seem to be unrelated, looking at the other PRs.
I also don't see a way to add test for this PR's changes, as it won't be possible to boot xen system in CI.

@gador
Copy link

gador commented Nov 12, 2024

I finally had time to try and boot into this PR.
I still get the exact same error described by me here NixOS/nixpkgs#350051

I have lanzaboote flake input pointed to github:CertainLach/lanzaboote/feat/xen"; and virtualisation.xen.enable = true; and it still gives me the same error messages.

I also now got
systemd-logind[2455]: Parsed PE file '/boot/EFI/Linux/xen-generation-395-4jk5grj5thec6zlqt5jfhpplmes6lsb5tdwyevfyx37hqfqgehcq.efi' is not a UKI. which is new.

Hope this helps. Let me know if I can/should do more debugging

@CertainLach
Copy link
Author

I also now got
systemd-logind[2455]: Parsed PE file '/boot/EFI/Linux/xen-generation-395-4jk5grj5thec6zlqt5jfhpplmes6lsb5tdwyevfyx37hqfqgehcq.efi' is not a UKI. which is new.

This is normal, as generated image is not a kernel image (K in UKI), instead it is booted from .conf entry in /boot/entries

Do you see new (With xen hypervisor) entry appearing in bootloader menu?

@gador
Copy link

gador commented Nov 12, 2024

You got it. I didn't select the new generation on boot. I have xen enabled now, thanks!

Do you know how to set it as default? Usually, the new generations always get selected by default, but this time, it didn't?

Also, maybe unrelated, I got a kernel bug, which I've never had before:

Nov 12 15:04:59 framework kernel: cros_ec_lpcs cros_ec_lpcs.0: bad packet checksum e7
Nov 12 15:05:06 framework kernel: list_del corruption. next->prev should be ffffea000c18fb08, but was ffffea000c580188. (next=ffffea000c580208)
Nov 12 15:05:06 framework kernel: ------------[ cut here ]------------
Nov 12 15:05:06 framework kernel: kernel BUG at lib/list_debug.c:65!

@CertainLach
Copy link
Author

Do you know how to set it as default? Usually, the new generations always get selected by default, but this time, it didn't?

(With xen support) entry should be on top, it isn't for you?

@gador
Copy link

gador commented Nov 12, 2024

It is. It just isn't chosen as default (The second entry is selected by default)

@CertainLach
Copy link
Author

After rebase, noticed that GC does no longer work, fixed that.

Latest commit might also change the ordering issue, though I haven't encountered that, probably due to customized loader.conf

@gador
Copy link

gador commented Nov 20, 2024

Tried after your change: Now, no new entry is generated and also the old ones do not get removed

@CertainLach
Copy link
Author

Entries generated using the old version need to be removed manually,

rm /boot/loader/entries/xen-* /boot/efi/Linux/xen-*

As for entries not generating - can you try to change your nixos configuration a little, it is possible it doesn't think that is is the new system generation for some reason.

@gador
Copy link

gador commented Nov 20, 2024

Removing the entries manually did work, thanks.
I did change my config (.e.g I added the suggested kernel lines from NixOS/nixpkgs#350051 (comment) ) and a new generation is created and selected by default, but nothing with xen only the normal entry.

tree /boot
/boot
├── EFI
│   ├── BOOT
│   │   └── BOOTX64.EFI
│   ├── Linux
│   │   ├── nixos-generation-398-sdkuoi3d6zicbjwluebgrxmfye6igjqlbtm7asju2lkyy7fl5cla.efi
│   │   ├── nixos-generation-399-u2sh6yrm6ktufejqk7npbdwvnjgdrrwmlhac6pldqdndsctxrgma.efi
│   │   └── nixos-generation-400-pbgxqz6jxzch6ritom3run4dipbas4fk3ucctvjqewvm4z7raazq.efi
│   ├── nixos
│   │   ├── initrd-6.6.60-7as2p44fk6ei7v2kguhgmfoi7ika6a5cehwr35uez4ntf2xuspca.efi
│   │   ├── initrd-6.6.60-amvb5kbxg4orhupuosvpyd7wvd7lyig4vwogytc4wccycqqbjwba.efi
│   │   └── kernel-6.6.60-be6ao25tnwttdfers5y5vmp7irgpmv2pdn4ssvgxrfnyn6qcxtha.efi
│   └── systemd
│       └── systemd-bootx64.efi
└── loader
    ├── entries
    ├── entries.srel
    ├── loader.conf
    └── random-seed

And commands like xl don't work, just as I get errors during boot like Failed to insert module 'xenfs': No such device

@CertainLach
Copy link
Author

Huh, are you sure you install correct lanzaboote?

inputs.lanzaboote.url = "github:CertainLach/lanzaboote/feat/xen";

Do you have xen extension in your /run/current-system/boot.json?

@gador
Copy link

gador commented Nov 20, 2024

arrg. Im dump. When I switched back to the original branch I commented your input and forgot to uncomment :-/
So, now I have the new xen generations and it didn't freeze up until now, so fingers crossed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants