1{
2 lib,
3 config,
4 options,
5 ...
6}:
7let
8 inherit (builtins) hasAttr;
9 inherit (lib) mkIf;
10 cfg = config.hardware.cpu.x86.msr;
11 opt = options.hardware.cpu.x86.msr;
12 defaultGroup = "msr";
13 isDefaultGroup = cfg.group == defaultGroup;
14 set = "to set for devices of the `msr` kernel subsystem.";
15
16 # Generates `foo=bar` parameters to pass to the kernel.
17 # If `module = baz` is passed, generates `baz.foo=bar`.
18 # Adds double quotes on demand to handle `foo="bar baz"`.
19 kernelParam =
20 {
21 module ? null,
22 }:
23 name: value:
24 assert lib.asserts.assertMsg (
25 !lib.strings.hasInfix "=" name
26 ) "kernel parameter cannot have '=' in name";
27 let
28 key = (if module == null then "" else module + ".") + name;
29 valueString = lib.generators.mkValueStringDefault { } value;
30 quotedValueString =
31 if lib.strings.hasInfix " " valueString then
32 lib.strings.escape [ "\"" ] valueString
33 else
34 valueString;
35 in
36 "${key}=${quotedValueString}";
37 msrKernelParam = kernelParam { module = "msr"; };
38in
39{
40 options.hardware.cpu.x86.msr =
41 with lib.options;
42 with lib.types;
43 {
44 enable = mkEnableOption "the `msr` (Model-Specific Registers) kernel module and configure `udev` rules for its devices (usually `/dev/cpu/*/msr`)";
45 owner = mkOption {
46 type = str;
47 default = "root";
48 example = "nobody";
49 description = "Owner ${set}";
50 };
51 group = mkOption {
52 type = str;
53 default = defaultGroup;
54 example = "nobody";
55 description = "Group ${set}";
56 };
57 mode = mkOption {
58 type = str;
59 default = "0640";
60 example = "0660";
61 description = "Mode ${set}";
62 };
63 settings = mkOption {
64 type = submodule {
65 freeformType = attrsOf (oneOf [
66 bool
67 int
68 str
69 ]);
70 options.allow-writes = mkOption {
71 type = nullOr (enum [
72 "on"
73 "off"
74 ]);
75 default = null;
76 description = "Whether to allow writes to MSRs (`\"on\"`) or not (`\"off\"`).";
77 };
78 };
79 default = { };
80 description = "Parameters for the `msr` kernel module.";
81 };
82 };
83
84 config = mkIf cfg.enable {
85 assertions = [
86 {
87 assertion = hasAttr cfg.owner config.users.users;
88 message = "Owner '${cfg.owner}' set in `${opt.owner}` is not configured via `${options.users.users}.\"${cfg.owner}\"`.";
89 }
90 {
91 assertion = isDefaultGroup || (hasAttr cfg.group config.users.groups);
92 message = "Group '${cfg.group}' set in `${opt.group}` is not configured via `${options.users.groups}.\"${cfg.group}\"`.";
93 }
94 ];
95
96 boot = {
97 kernelModules = [ "msr" ];
98 kernelParams = lib.attrsets.mapAttrsToList msrKernelParam (
99 lib.attrsets.filterAttrs (_: value: value != null) cfg.settings
100 );
101 };
102
103 users.groups.${cfg.group} = mkIf isDefaultGroup { };
104
105 services.udev.extraRules = ''
106 SUBSYSTEM=="msr", OWNER="${cfg.owner}", GROUP="${cfg.group}", MODE="${cfg.mode}"
107 '';
108 };
109
110 meta = with lib; {
111 maintainers = with maintainers; [ lorenzleutgeb ];
112 };
113}