1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 cfg = config.hardware.rasdaemon;
8
9in
10{
11 options.hardware.rasdaemon = {
12
13 enable = mkEnableOption (lib.mdDoc "RAS logging daemon");
14
15 record = mkOption {
16 type = types.bool;
17 default = true;
18 description = lib.mdDoc "record events via sqlite3, required for ras-mc-ctl";
19 };
20
21 mainboard = mkOption {
22 type = types.lines;
23 default = "";
24 description = lib.mdDoc "Custom mainboard description, see {manpage}`ras-mc-ctl(8)` for more details.";
25 example = ''
26 vendor = ASRock
27 model = B450M Pro4
28
29 # it should default to such values from
30 # /sys/class/dmi/id/board_[vendor|name]
31 # alternatively one can supply a script
32 # that returns the same format as above
33
34 script = <path to script>
35 '';
36 };
37
38 # TODO, accept `rasdaemon.labels = " ";` or `rasdaemon.labels = { dell = " "; asrock = " "; };'
39
40 labels = mkOption {
41 type = types.lines;
42 default = "";
43 description = lib.mdDoc "Additional memory module label descriptions to be placed in /etc/ras/dimm_labels.d/labels";
44 example = ''
45 # vendor and model may be shown by 'ras-mc-ctl --mainboard'
46 vendor: ASRock
47 product: To Be Filled By O.E.M.
48 model: B450M Pro4
49 # these labels are names for the motherboard slots
50 # the numbers may be shown by `ras-mc-ctl --error-count`
51 # they are mc:csrow:channel
52 DDR4_A1: 0.2.0; DDR4_B1: 0.2.1;
53 DDR4_A2: 0.3.0; DDR4_B2: 0.3.1;
54 '';
55 };
56
57 config = mkOption {
58 type = types.lines;
59 default = "";
60 description = lib.mdDoc ''
61 rasdaemon configuration, currently only used for CE PFA
62 for details, read rasdaemon.outPath/etc/sysconfig/rasdaemon's comments
63 '';
64 example = ''
65 # defaults from included config
66 PAGE_CE_REFRESH_CYCLE="24h"
67 PAGE_CE_THRESHOLD="50"
68 PAGE_CE_ACTION="soft"
69 '';
70 };
71
72 extraModules = mkOption {
73 type = types.listOf types.str;
74 default = [];
75 description = lib.mdDoc "extra kernel modules to load";
76 example = [ "i7core_edac" ];
77 };
78
79 testing = mkEnableOption (lib.mdDoc "error injection infrastructure");
80 };
81
82 config = mkIf cfg.enable {
83
84 environment.etc = {
85 "ras/mainboard" = {
86 enable = cfg.mainboard != "";
87 text = cfg.mainboard;
88 };
89 # TODO, handle multiple cfg.labels.brand = " ";
90 "ras/dimm_labels.d/labels" = {
91 enable = cfg.labels != "";
92 text = cfg.labels;
93 };
94 "sysconfig/rasdaemon" = {
95 enable = cfg.config != "";
96 text = cfg.config;
97 };
98 };
99 environment.systemPackages = [ pkgs.rasdaemon ]
100 ++ optionals (cfg.testing) (with pkgs.error-inject; [
101 edac-inject
102 mce-inject
103 aer-inject
104 ]);
105
106 boot.initrd.kernelModules = cfg.extraModules
107 ++ optionals (cfg.testing) [
108 # edac_core and amd64_edac should get loaded automatically
109 # i7core_edac may not be, and may not be required, but should load successfully
110 "edac_core"
111 "amd64_edac"
112 "i7core_edac"
113 "mce-inject"
114 "aer-inject"
115 ];
116
117 boot.kernelPatches = optionals (cfg.testing) [{
118 name = "rasdaemon-tests";
119 patch = null;
120 extraConfig = ''
121 EDAC_DEBUG y
122 X86_MCE_INJECT y
123
124 PCIEPORTBUS y
125 PCIEAER y
126 PCIEAER_INJECT y
127 '';
128 }];
129
130 # i tried to set up a group for this
131 # but rasdaemon needs higher permissions?
132 # `rasdaemon: Can't locate a mounted debugfs`
133
134 # most of this taken from src/misc/
135 systemd.services = {
136 rasdaemon = {
137 description = "the RAS logging daemon";
138 documentation = [ "man:rasdaemon(1)" ];
139 wantedBy = [ "multi-user.target" ];
140
141 serviceConfig = {
142 StateDirectory = optionalString (cfg.record) "rasdaemon";
143
144 ExecStart = "${pkgs.rasdaemon}/bin/rasdaemon --foreground"
145 + optionalString (cfg.record) " --record";
146 ExecStop = "${pkgs.rasdaemon}/bin/rasdaemon --disable";
147 Restart = "on-abort";
148
149 # src/misc/rasdaemon.service.in shows this:
150 # ExecStartPost = ${pkgs.rasdaemon}/bin/rasdaemon --enable
151 # but that results in unpredictable existence of the database
152 # and everything seems to be enabled without this...
153 };
154 };
155 ras-mc-ctl = mkIf (cfg.labels != "") {
156 description = "register DIMM labels on startup";
157 documentation = [ "man:ras-mc-ctl(8)" ];
158 wantedBy = [ "multi-user.target" ];
159 serviceConfig = {
160 Type = "oneshot";
161 ExecStart = "${pkgs.rasdaemon}/bin/ras-mc-ctl --register-labels";
162 RemainAfterExit = true;
163 };
164 };
165 };
166 };
167
168 meta.maintainers = [ maintainers.evils ];
169
170}