1# Xen Project Hypervisor (Dom0) support.
2
3{
4 config,
5 lib,
6 pkgs,
7 ...
8}:
9
10let
11 inherit (builtins) readFile;
12 inherit (lib.modules) mkRemovedOptionModule mkRenamedOptionModule mkIf;
13 inherit (lib.options)
14 mkOption
15 mkEnableOption
16 literalExpression
17 mkPackageOption
18 ;
19 inherit (lib.types)
20 listOf
21 str
22 ints
23 lines
24 enum
25 path
26 submodule
27 addCheck
28 float
29 bool
30 int
31 nullOr
32 ;
33 inherit (lib.lists) optional optionals;
34 inherit (lib.strings) hasSuffix optionalString;
35 inherit (lib.meta) getExe;
36 inherit (lib.attrsets) optionalAttrs;
37 inherit (lib.trivial) boolToString;
38 inherit (lib.teams.xen) members;
39
40 cfg = config.virtualisation.xen;
41
42 xenBootBuilder = pkgs.writeShellApplication {
43 name = "xenBootBuilder";
44 runtimeInputs =
45 (with pkgs; [
46 binutils
47 coreutils
48 findutils
49 gawk
50 gnugrep
51 gnused
52 jq
53 ])
54 ++ optionals (cfg.efi.bootBuilderVerbosity == "info") (
55 with pkgs;
56 [
57 bat
58 diffutils
59 ]
60 );
61 runtimeEnv = {
62 efiMountPoint = config.boot.loader.efi.efiSysMountPoint;
63 };
64
65 # We disable SC2016 because we don't want to expand the regexes in the sed commands.
66 excludeShellChecks = [ "SC2016" ];
67
68 text = readFile ./xen-boot-builder.sh;
69 };
70in
71
72{
73 imports = [
74 (mkRemovedOptionModule
75 [
76 "virtualisation"
77 "xen"
78 "bridge"
79 "name"
80 ]
81 "The Xen Network Bridge options are currently unavailable. Please set up your own bridge manually."
82 )
83 (mkRemovedOptionModule
84 [
85 "virtualisation"
86 "xen"
87 "bridge"
88 "address"
89 ]
90 "The Xen Network Bridge options are currently unavailable. Please set up your own bridge manually."
91 )
92 (mkRemovedOptionModule
93 [
94 "virtualisation"
95 "xen"
96 "bridge"
97 "prefixLength"
98 ]
99 "The Xen Network Bridge options are currently unavailable. Please set up your own bridge manually."
100 )
101 (mkRemovedOptionModule
102 [
103 "virtualisation"
104 "xen"
105 "bridge"
106 "forwardDns"
107 ]
108 "The Xen Network Bridge options are currently unavailable. Please set up your own bridge manually."
109 )
110 (mkRenamedOptionModule
111 [
112 "virtualisation"
113 "xen"
114 "qemu-package"
115 ]
116 [
117 "virtualisation"
118 "xen"
119 "qemu"
120 "package"
121 ]
122 )
123 (mkRenamedOptionModule
124 [
125 "virtualisation"
126 "xen"
127 "package-qemu"
128 ]
129 [
130 "virtualisation"
131 "xen"
132 "qemu"
133 "package"
134 ]
135 )
136 (mkRenamedOptionModule
137 [
138 "virtualisation"
139 "xen"
140 "stored"
141 ]
142 [
143 "virtualisation"
144 "xen"
145 "store"
146 "path"
147 ]
148 )
149 ];
150
151 ## Interface ##
152
153 options.virtualisation.xen = {
154
155 enable = mkEnableOption "the Xen Project Hypervisor, a virtualisation technology defined as a *type-1 hypervisor*, which allows multiple virtual machines, known as *domains*, to run concurrently on the physical machine. NixOS runs as the privileged *Domain 0*. This option requires a reboot into a Xen kernel to take effect";
156
157 debug = mkEnableOption "Xen debug features for Domain 0. This option enables some hidden debugging tests and features, and should not be used in production";
158
159 trace = mkOption {
160 type = bool;
161 default = cfg.debug;
162 defaultText = literalExpression "false";
163 example = true;
164 description = "Whether to enable Xen debug tracing and logging for Domain 0.";
165 };
166
167 package = mkPackageOption pkgs "Xen Hypervisor" { default = [ "xen" ]; };
168
169 qemu = {
170 package = mkPackageOption pkgs "QEMU (with Xen Hypervisor support)" {
171 default = [ "qemu_xen" ];
172 };
173 pidFile = mkOption {
174 type = path;
175 default = "/run/xen/qemu-dom0.pid";
176 example = "/var/run/xen/qemu-dom0.pid";
177 description = "Path to the QEMU PID file.";
178 };
179 };
180
181 bootParams = mkOption {
182 default = [ ];
183 example = ''
184 [
185 "iommu=force:true,qinval:true,debug:true"
186 "noreboot=true"
187 "vga=ask"
188 ]
189 '';
190 type = listOf str;
191 description = ''
192 Xen Command Line parameters passed to Domain 0 at boot time.
193 Note: these are different from `boot.kernelParams`. See
194 the [Xen documentation](https://xenbits.xenproject.org/docs/unstable/misc/xen-command-line.html) for more information.
195 '';
196 };
197
198 efi = {
199 bootBuilderVerbosity = mkOption {
200 type = enum [
201 "default"
202 "info"
203 "debug"
204 "quiet"
205 ];
206 default = "default";
207 example = "info";
208 description = ''
209 The EFI boot entry builder script should be called with exactly one of the following arguments in order to specify its verbosity:
210
211 - `quiet` supresses all messages.
212
213 - `default` adds a simple "Installing Xen Project Hypervisor boot entries...done." message to the script.
214
215 - `info` is the same as `default`, but it also prints a diff with information on which generations were altered.
216 - This option adds two extra dependencies to the script: `diffutils` and `bat`.
217
218 - `debug` prints information messages for every single step of the script.
219
220 This option does not alter the actual functionality of the script, just the number of messages printed when rebuilding the system.
221 '';
222 };
223
224 path = mkOption {
225 type = path;
226 default = "${cfg.package.boot}/${cfg.package.efi}";
227 defaultText = literalExpression "\${config.virtualisation.xen.package.boot}/\${config.virtualisation.xen.package.efi}";
228 example = literalExpression "\${config.virtualisation.xen.package}/boot/efi/efi/nixos/xen-\${config.virtualisation.xen.package.version}.efi";
229 description = ''
230 Path to xen.efi. `pkgs.xen` is patched to install the xen.efi file
231 on `$boot/boot/xen.efi`, but an unpatched Xen build may install it
232 somewhere else, such as `$out/boot/efi/efi/nixos/xen.efi`. Unless
233 you're building your own Xen derivation, you should leave this
234 option as the default value.
235 '';
236 };
237 };
238
239 dom0Resources = {
240 maxVCPUs = mkOption {
241 default = 0;
242 example = 4;
243 type = ints.unsigned;
244 description = ''
245 Amount of virtual CPU cores allocated to Domain 0 on boot.
246 If set to 0, all cores are assigned to Domain 0, and
247 unprivileged domains will compete with Domain 0 for CPU time.
248 '';
249 };
250
251 memory = mkOption {
252 default = 0;
253 example = 512;
254 type = ints.unsigned;
255 description = ''
256 Amount of memory (in MiB) allocated to Domain 0 on boot.
257 If set to 0, all memory is assigned to Domain 0, and
258 unprivileged domains will compete with Domain 0 for free RAM.
259 '';
260 };
261
262 maxMemory = mkOption {
263 default = cfg.dom0Resources.memory;
264 defaultText = literalExpression "config.virtualisation.xen.dom0Resources.memory";
265 example = 1024;
266 type = ints.unsigned;
267 description = ''
268 Maximum amount of memory (in MiB) that Domain 0 can
269 dynamically allocate to itself. Does nothing if set
270 to the same amount as virtualisation.xen.memory, or
271 if that option is set to 0.
272 '';
273 };
274 };
275
276 domains = {
277 extraConfig = mkOption {
278 type = lines;
279 default = "";
280 example = ''
281 XENDOMAINS_SAVE=/persist/xen/save
282 XENDOMAINS_RESTORE=false
283 XENDOMAINS_CREATE_USLEEP=10000000
284 '';
285 description = ''
286 Options defined here will override the defaults for xendomains.
287 The default options can be seen in the file included from
288 /etc/default/xendomains.
289 '';
290 };
291 };
292
293 store = {
294 path = mkOption {
295 type = path;
296 default = "${cfg.package}/bin/oxenstored";
297 defaultText = literalExpression "\${config.virtualisation.xen.package}/bin/oxenstored";
298 example = literalExpression "\${config.virtualisation.xen.package}/bin/xenstored";
299 description = ''
300 Path to the Xen Store Daemon. This option is useful to
301 switch between the legacy C-based Xen Store Daemon, and
302 the newer OCaml-based Xen Store Daemon, `oxenstored`.
303 '';
304 };
305 type = mkOption {
306 type = enum [
307 "c"
308 "ocaml"
309 ];
310 default = if (hasSuffix "oxenstored" cfg.store.path) then "ocaml" else "c";
311 internal = true;
312 readOnly = true;
313 description = "Helper internal option that determines the type of the Xen Store Daemon based on cfg.store.path.";
314 };
315 settings = mkOption {
316 default = { };
317 example = {
318 enableMerge = false;
319 quota.maxWatchEvents = 2048;
320 quota.enable = true;
321 conflict.maxHistorySeconds = 0.12;
322 conflict.burstLimit = 15.0;
323 xenstored.log.file = "/dev/null";
324 xenstored.log.level = "info";
325 };
326 description = ''
327 The OCaml-based Xen Store Daemon configuration. This
328 option does nothing with the C-based `xenstored`.
329 '';
330 type = submodule {
331 options = {
332 pidFile = mkOption {
333 default = "/run/xen/xenstored.pid";
334 example = "/var/run/xen/xenstored.pid";
335 type = path;
336 description = "Path to the Xen Store Daemon PID file.";
337 };
338 testEAGAIN = mkOption {
339 default = cfg.debug;
340 defaultText = literalExpression "config.virtualisation.xen.debug";
341 example = true;
342 type = bool;
343 visible = false;
344 description = "Randomly fail a transaction with EAGAIN. This option is used for debugging purposes only.";
345 };
346 enableMerge = mkOption {
347 default = true;
348 example = false;
349 type = bool;
350 description = "Whether to enable transaction merge support.";
351 };
352 conflict = {
353 burstLimit = mkOption {
354 default = 5.0;
355 example = 15.0;
356 type = addCheck (
357 float
358 // {
359 name = "nonnegativeFloat";
360 description = "nonnegative floating point number, meaning >=0";
361 descriptionClass = "nonRestrictiveClause";
362 }
363 ) (n: n >= 0);
364 description = ''
365 Limits applied to domains whose writes cause other domains' transaction
366 commits to fail. Must include decimal point.
367
368 The burst limit is the number of conflicts a domain can cause to
369 fail in a short period; this value is used for both the initial and
370 the maximum value of each domain's conflict-credit, which falls by
371 one point for each conflict caused, and when it reaches zero the
372 domain's requests are ignored.
373 '';
374 };
375 maxHistorySeconds = mkOption {
376 default = 5.0e-2;
377 example = 1.0;
378 type = addCheck (float // { description = "nonnegative floating point number, meaning >=0"; }) (
379 n: n >= 0
380 );
381 description = ''
382 Limits applied to domains whose writes cause other domains' transaction
383 commits to fail. Must include decimal point.
384
385 The conflict-credit is replenished over time:
386 one point is issued after each conflict.maxHistorySeconds, so this
387 is the minimum pause-time during which a domain will be ignored.
388 '';
389 };
390 rateLimitIsAggregate = mkOption {
391 default = true;
392 example = false;
393 type = bool;
394 description = ''
395 If the conflict.rateLimitIsAggregate option is `true`, then after each
396 tick one point of conflict-credit is given to just one domain: the
397 one at the front of the queue. If `false`, then after each tick each
398 domain gets a point of conflict-credit.
399
400 In environments where it is known that every transaction will
401 involve a set of nodes that is writable by at most one other domain,
402 then it is safe to set this aggregate limit flag to `false` for better
403 performance. (This can be determined by considering the layout of
404 the xenstore tree and permissions, together with the content of the
405 transactions that require protection.)
406
407 A transaction which involves a set of nodes which can be modified by
408 multiple other domains can suffer conflicts caused by any of those
409 domains, so the flag must be set to `true`.
410 '';
411 };
412 };
413 perms = {
414 enable = mkOption {
415 default = true;
416 example = false;
417 type = bool;
418 description = "Whether to enable the node permission system.";
419 };
420 enableWatch = mkOption {
421 default = true;
422 example = false;
423 type = bool;
424 description = ''
425 Whether to enable the watch permission system.
426
427 When this is set to `true`, unprivileged guests can only get watch events
428 for xenstore entries that they would've been able to read.
429
430 When this is set to `false`, unprivileged guests may get watch events
431 for xenstore entries that they cannot read. The watch event contains
432 only the entry name, not the value.
433 This restores behaviour prior to [XSA-115](https://xenbits.xenproject.org/xsa/advisory-115.html).
434 '';
435 };
436 };
437 quota = {
438 enable = mkOption {
439 default = true;
440 example = false;
441 type = bool;
442 description = "Whether to enable the quota system.";
443 };
444 maxEntity = mkOption {
445 default = 1000;
446 example = 1024;
447 type = ints.positive;
448 description = "Entity limit for transactions.";
449 };
450 maxSize = mkOption {
451 default = 2048;
452 example = 4096;
453 type = ints.positive;
454 description = "Size limit for transactions.";
455 };
456 maxWatch = mkOption {
457 default = 100;
458 example = 256;
459 type = ints.positive;
460 description = "Maximum number of watches by the Xenstore Watchdog.";
461 };
462 transaction = mkOption {
463 default = 10;
464 example = 50;
465 type = ints.positive;
466 description = "Maximum number of transactions.";
467 };
468 maxRequests = mkOption {
469 default = 1024;
470 example = 1024;
471 type = ints.positive;
472 description = "Maximum number of requests per transaction.";
473 };
474 maxPath = mkOption {
475 default = 1024;
476 example = 1024;
477 type = ints.positive;
478 description = "Path limit for the quota system.";
479 };
480 maxOutstanding = mkOption {
481 default = 1024;
482 example = 1024;
483 type = ints.positive;
484 description = "Maximum outstanding requests, i.e. in-flight requests / domain.";
485 };
486 maxWatchEvents = mkOption {
487 default = 1024;
488 example = 2048;
489 type = ints.positive;
490 description = "Maximum number of outstanding watch events per watch.";
491 };
492 };
493 persistent = mkOption {
494 default = false;
495 example = true;
496 type = bool;
497 description = "Whether to activate the filed base backend.";
498 };
499 xenstored = {
500 log = {
501 file = mkOption {
502 default = "/var/log/xen/xenstored.log";
503 example = "/dev/null";
504 type = path;
505 description = "Path to the Xen Store log file.";
506 };
507 level = mkOption {
508 default = if cfg.trace then "debug" else null;
509 defaultText = literalExpression "if (config.virtualisation.xen.trace == true) then \"debug\" else null";
510 example = "error";
511 type = nullOr (enum [
512 "debug"
513 "info"
514 "warn"
515 "error"
516 ]);
517 description = "Logging level for the Xen Store.";
518 };
519 # The hidden options below have no upstream documentation whatsoever.
520 # The nb* options appear to alter the log rotation behaviour, and
521 # the specialOps option appears to affect the Xenbus logging logic.
522 nbFiles = mkOption {
523 default = 10;
524 example = 16;
525 type = int;
526 visible = false;
527 description = "Set `xenstored-log-nb-files`.";
528 };
529 };
530 accessLog = {
531 file = mkOption {
532 default = "/var/log/xen/xenstored-access.log";
533 example = "/var/log/security/xenstored-access.log";
534 type = path;
535 description = "Path to the Xen Store access log file.";
536 };
537 nbLines = mkOption {
538 default = 13215;
539 example = 16384;
540 type = int;
541 visible = false;
542 description = "Set `access-log-nb-lines`.";
543 };
544 nbChars = mkOption {
545 default = 180;
546 example = 256;
547 type = int;
548 visible = false;
549 description = "Set `acesss-log-nb-chars`.";
550 };
551 specialOps = mkOption {
552 default = false;
553 example = true;
554 type = bool;
555 visible = false;
556 description = "Set `access-log-special-ops`.";
557 };
558 };
559 xenfs = {
560 kva = mkOption {
561 default = "/proc/xen/xsd_kva";
562 example = cfg.store.settings.xenstored.xenfs.kva;
563 type = path;
564 visible = false;
565 description = ''
566 Path to the Xen Store Daemon KVA location inside the XenFS pseudo-filesystem.
567 While it is possible to alter this value, some drivers may be hardcoded to follow the default paths.
568 '';
569 };
570 port = mkOption {
571 default = "/proc/xen/xsd_port";
572 example = cfg.store.settings.xenstored.xenfs.port;
573 type = path;
574 visible = false;
575 description = ''
576 Path to the Xen Store Daemon userspace port inside the XenFS pseudo-filesystem.
577 While it is possible to alter this value, some drivers may be hardcoded to follow the default paths.
578 '';
579 };
580 };
581 };
582 ringScanInterval = mkOption {
583 default = 20;
584 example = 30;
585 type = addCheck (
586 int
587 // {
588 name = "nonzeroInt";
589 description = "nonzero signed integer, meaning !=0";
590 descriptionClass = "nonRestrictiveClause";
591 }
592 ) (n: n != 0);
593 description = ''
594 Perodic scanning for all the rings as a safenet for lazy clients.
595 Define the interval in seconds; set to a negative integer to disable.
596 '';
597 };
598 };
599 };
600 };
601 };
602 };
603
604 ## Implementation ##
605
606 config = mkIf cfg.enable {
607 assertions = [
608 {
609 assertion = pkgs.stdenv.hostPlatform.isx86_64;
610 message = "Xen is currently not supported on ${pkgs.stdenv.hostPlatform.system}.";
611 }
612 {
613 assertion =
614 config.boot.loader.systemd-boot.enable
615 || (config.boot ? lanzaboote) && config.boot.lanzaboote.enable;
616 message = "Xen only supports booting on systemd-boot or Lanzaboote.";
617 }
618 {
619 assertion = config.boot.initrd.systemd.enable;
620 message = "Xen does not support the legacy script-based Stage 1 initrd.";
621 }
622 {
623 assertion = cfg.dom0Resources.maxMemory >= cfg.dom0Resources.memory;
624 message = ''
625 You have allocated more memory to dom0 than virtualisation.xen.dom0Resources.maxMemory
626 allows for. Please increase the maximum memory limit, or decrease the default memory allocation.
627 '';
628 }
629 {
630 assertion = cfg.debug -> cfg.trace;
631 message = "Xen's debugging features are enabled, but logging is disabled. This is most likely not what you want.";
632 }
633 {
634 assertion = cfg.store.settings.quota.maxWatchEvents >= cfg.store.settings.quota.maxOutstanding;
635 message = ''
636 Upstream Xen recommends that maxWatchEvents be equal to or greater than maxOutstanding,
637 in order to mitigate denial of service attacks from malicious frontends.
638 '';
639 }
640 ];
641
642 virtualisation.xen.bootParams =
643 optionals cfg.trace [
644 "loglvl=all"
645 "guest_loglvl=all"
646 ]
647 ++
648 optional (cfg.dom0Resources.memory != 0)
649 "dom0_mem=${toString cfg.dom0Resources.memory}M${
650 optionalString (
651 cfg.dom0Resources.memory != cfg.dom0Resources.maxMemory
652 ) ",max:${toString cfg.dom0Resources.maxMemory}M"
653 }"
654 ++ optional (
655 cfg.dom0Resources.maxVCPUs != 0
656 ) "dom0_max_vcpus=${toString cfg.dom0Resources.maxVCPUs}";
657
658 boot = {
659 kernelModules = [
660 "xen-evtchn"
661 "xen-gntdev"
662 "xen-gntalloc"
663 "xen-blkback"
664 "xen-netback"
665 "xen-pciback"
666 "evtchn"
667 "gntdev"
668 "netbk"
669 "blkbk"
670 "xen-scsibk"
671 "usbbk"
672 "pciback"
673 "xen-acpi-processor"
674 "blktap2"
675 "tun"
676 "netxen_nic"
677 "xen_wdt"
678 "xen-acpi-processor"
679 "xen-privcmd"
680 "xen-scsiback"
681 "xenfs"
682 ];
683
684 # The xenfs module is needed to mount /proc/xen.
685 initrd.kernelModules = [ "xenfs" ];
686
687 # Increase the number of loopback devices from the default (8),
688 # which is way too small because every VM virtual disk requires a
689 # loopback device.
690 extraModprobeConfig = ''
691 options loop max_loop=64
692 '';
693
694 # Xen Bootspec extension. This extension allows NixOS bootloaders to
695 # fetch the `xen.efi` path and access the `cfg.bootParams` option.
696 bootspec.extensions = {
697 "org.xenproject.bootspec.v1" = {
698 xen = cfg.efi.path;
699 xenParams = cfg.bootParams;
700 };
701 };
702
703 # See the `xenBootBuilder` script in the main `let...in` statement of this file.
704 loader.systemd-boot.extraInstallCommands = ''
705 ${getExe xenBootBuilder} ${cfg.efi.bootBuilderVerbosity}
706 '';
707 };
708
709 # Domain 0 requires a pvops-enabled kernel.
710 # All NixOS kernels come with this enabled by default; this is merely a sanity check.
711 system.requiredKernelConfig = with config.lib.kernelConfig; [
712 (isYes "XEN")
713 (isYes "X86_IO_APIC")
714 (isYes "ACPI")
715 (isYes "XEN_DOM0")
716 (isYes "PCI_XEN")
717 (isYes "XEN_DEV_EVTCHN")
718 (isYes "XENFS")
719 (isYes "XEN_COMPAT_XENFS")
720 (isYes "XEN_SYS_HYPERVISOR")
721 (isYes "XEN_GNTDEV")
722 (isYes "XEN_BACKEND")
723 (isModule "XEN_NETDEV_BACKEND")
724 (isModule "XEN_BLKDEV_BACKEND")
725 (isModule "XEN_PCIDEV_BACKEND")
726 (isYes "XEN_BALLOON")
727 (isYes "XEN_SCRUB_PAGES")
728 ];
729
730 environment = {
731 systemPackages = [
732 cfg.package
733 cfg.qemu.package
734 ];
735 etc =
736 # Set up Xen Domain 0 configuration files.
737 {
738 "xen/xl.conf".source = "${cfg.package}/etc/xen/xl.conf"; # TODO: Add options to configure xl.conf declaratively. It's worth considering making a new "xl value" type, as it could be reused to produce xl.cfg (domain definition) files.
739 "xen/scripts-xen" = {
740 source = "${cfg.package}/etc/xen/scripts/*";
741 target = "xen/scripts";
742 };
743 "default/xencommons".text = ''
744 source ${cfg.package}/etc/default/xencommons
745
746 XENSTORED="${cfg.store.path}"
747 QEMU_XEN="${cfg.qemu.package}/${cfg.qemu.package.qemu-system-i386}"
748 ${optionalString cfg.trace ''
749 XENSTORED_TRACE=yes
750 XENCONSOLED_TRACE=all
751 ''}
752 '';
753 "default/xendomains".text = ''
754 source ${cfg.package}/etc/default/xendomains
755
756 ${cfg.domains.extraConfig}
757 '';
758 }
759 # The OCaml-based Xen Store Daemon requires /etc/xen/oxenstored.conf to start.
760 // optionalAttrs (cfg.store.type == "ocaml") {
761 "xen/oxenstored.conf".text = ''
762 pid-file = ${cfg.store.settings.pidFile}
763 test-eagain = ${boolToString cfg.store.settings.testEAGAIN}
764 merge-activate = ${toString cfg.store.settings.enableMerge}
765 conflict-burst-limit = ${toString cfg.store.settings.conflict.burstLimit}
766 conflict-max-history-seconds = ${toString cfg.store.settings.conflict.maxHistorySeconds}
767 conflict-rate-limit-is-aggregate = ${toString cfg.store.settings.conflict.rateLimitIsAggregate}
768 perms-activate = ${toString cfg.store.settings.perms.enable}
769 perms-watch-activate = ${toString cfg.store.settings.perms.enableWatch}
770 quota-activate = ${toString cfg.store.settings.quota.enable}
771 quota-maxentity = ${toString cfg.store.settings.quota.maxEntity}
772 quota-maxsize = ${toString cfg.store.settings.quota.maxSize}
773 quota-maxwatch = ${toString cfg.store.settings.quota.maxWatch}
774 quota-transaction = ${toString cfg.store.settings.quota.transaction}
775 quota-maxrequests = ${toString cfg.store.settings.quota.maxRequests}
776 quota-path-max = ${toString cfg.store.settings.quota.maxPath}
777 quota-maxoutstanding = ${toString cfg.store.settings.quota.maxOutstanding}
778 quota-maxwatchevents = ${toString cfg.store.settings.quota.maxWatchEvents}
779 persistent = ${boolToString cfg.store.settings.persistent}
780 xenstored-log-file = ${cfg.store.settings.xenstored.log.file}
781 xenstored-log-level = ${
782 if isNull cfg.store.settings.xenstored.log.level then
783 "null"
784 else
785 cfg.store.settings.xenstored.log.level
786 }
787 xenstored-log-nb-files = ${toString cfg.store.settings.xenstored.log.nbFiles}
788 access-log-file = ${cfg.store.settings.xenstored.accessLog.file}
789 access-log-nb-lines = ${toString cfg.store.settings.xenstored.accessLog.nbLines}
790 acesss-log-nb-chars = ${toString cfg.store.settings.xenstored.accessLog.nbChars}
791 access-log-special-ops = ${boolToString cfg.store.settings.xenstored.accessLog.specialOps}
792 ring-scan-interval = ${toString cfg.store.settings.ringScanInterval}
793 xenstored-kva = ${cfg.store.settings.xenstored.xenfs.kva}
794 xenstored-port = ${cfg.store.settings.xenstored.xenfs.port}
795 '';
796 };
797 };
798
799 # Xen provides udev rules.
800 services.udev.packages = [ cfg.package ];
801
802 systemd = {
803 # Xen provides systemd units.
804 packages = [ cfg.package ];
805
806 mounts = [
807 {
808 description = "Mount /proc/xen files";
809 what = "xenfs";
810 where = "/proc/xen";
811 type = "xenfs";
812 unitConfig = {
813 ConditionPathExists = "/proc/xen";
814 RefuseManualStop = "true";
815 };
816 }
817 ];
818
819 services = {
820
821 # While this service is installed by the `xen` package, it shouldn't be used in dom0.
822 xendriverdomain.enable = false;
823
824 xenstored = {
825 wantedBy = [ "multi-user.target" ];
826 preStart = ''
827 export XENSTORED_ROOTDIR="/var/lib/xenstored"
828 rm -f "$XENSTORED_ROOTDIR"/tdb* &>/dev/null
829 mkdir -p /var/{run,log,lib}/xen
830 '';
831 };
832
833 xen-init-dom0 = {
834 restartIfChanged = false;
835 wantedBy = [ "multi-user.target" ];
836 };
837
838 xen-qemu-dom0-disk-backend = {
839 wantedBy = [ "multi-user.target" ];
840 serviceConfig = {
841 PIDFile = cfg.qemu.pidFile;
842 ExecStart = ''
843 ${cfg.qemu.package}/${cfg.qemu.package.qemu-system-i386} \
844 -xen-domid 0 -xen-attach -name dom0 -nographic -M xenpv \
845 -daemonize -monitor /dev/null -serial /dev/null -parallel \
846 /dev/null -nodefaults -no-user-config -pidfile \
847 ${cfg.qemu.pidFile}
848 '';
849 };
850 };
851
852 xenconsoled.wantedBy = [ "multi-user.target" ];
853
854 xen-watchdog = {
855 wantedBy = [ "multi-user.target" ];
856 serviceConfig = {
857 RestartSec = "1";
858 Restart = "on-failure";
859 };
860 };
861
862 xendomains = {
863 restartIfChanged = false;
864 path = [
865 cfg.package
866 cfg.qemu.package
867 ];
868 preStart = "mkdir -p /var/lock/subsys -m 755";
869 wantedBy = [ "multi-user.target" ];
870 };
871 };
872 };
873 };
874 meta.maintainers = members;
875}