1 /* SPDX-License-Identifier: BSD-3-Clause */
2 /* Copyright (c) 2024, Intel Corporation
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of the Intel Corporation nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /**
33 * @file ice_fw_logging.c
34 * @brief firmware logging sysctls
35 *
36 * Contains sysctls to enable and configure firmware logging debug support.
37 */
38
39 #include "ice_lib.h"
40 #include "ice_iflib.h"
41 #include <sys/queue.h>
42 #include <sys/sdt.h>
43
44 /*
45 * SDT provider for DTrace probes related to firmware logging events
46 */
47 SDT_PROVIDER_DEFINE(ice_fwlog);
48
49 /*
50 * SDT DTrace probe fired when a firmware log message is received over the
51 * AdminQ. It passes the buffer of the firwmare log message along with its
52 * length in bytes to the DTrace framework.
53 */
54 SDT_PROBE_DEFINE2(ice_fwlog, , , message, "uint8_t *", "int");
55
56 /*
57 * Helper function prototypes
58 */
59 static int ice_reconfig_fw_log(struct ice_softc *sc, struct ice_fwlog_cfg *cfg);
60
61 /*
62 * dynamic sysctl handlers
63 */
64 static int ice_sysctl_fwlog_set_cfg_options(SYSCTL_HANDLER_ARGS);
65 static int ice_sysctl_fwlog_log_resolution(SYSCTL_HANDLER_ARGS);
66 static int ice_sysctl_fwlog_register(SYSCTL_HANDLER_ARGS);
67 static int ice_sysctl_fwlog_module_log_severity(SYSCTL_HANDLER_ARGS);
68
69 /**
70 * ice_reconfig_fw_log - Re-program firmware logging configuration
71 * @sc: private softc structure
72 * @cfg: firmware log configuration to latch
73 *
74 * If the adminq is currently active, ask firmware to update the logging
75 * configuration. If the adminq is currently down, then do nothing. In this
76 * case, ice_init_hw() will re-configure firmware logging as soon as it brings
77 * up the adminq.
78 */
79 static int
ice_reconfig_fw_log(struct ice_softc * sc,struct ice_fwlog_cfg * cfg)80 ice_reconfig_fw_log(struct ice_softc *sc, struct ice_fwlog_cfg *cfg)
81 {
82 enum ice_status status;
83
84 ice_fwlog_init(&sc->hw, cfg);
85
86 if (!ice_check_sq_alive(&sc->hw, &sc->hw.adminq))
87 return (0);
88
89 if (!ice_fwlog_supported(&sc->hw))
90 return (0);
91
92 status = ice_fwlog_set(&sc->hw, cfg);
93 if (status) {
94 device_printf(sc->dev,
95 "Failed to reconfigure firmware logging, err %s aq_err %s\n",
96 ice_status_str(status),
97 ice_aq_str(sc->hw.adminq.sq_last_status));
98 return (ENODEV);
99 }
100
101 return (0);
102 }
103
104 #define ICE_SYSCTL_HELP_FWLOG_LOG_RESOLUTION \
105 "\nControl firmware message limit to send per ARQ event" \
106 "\t\nMin: 1" \
107 "\t\nMax: 128"
108
109 #define ICE_SYSCTL_HELP_FWLOG_ARQ_ENA \
110 "\nControl whether to enable/disable reporting to admin Rx queue" \
111 "\n0 - Enable firmware reporting via ARQ" \
112 "\n1 - Disable firmware reporting via ARQ"
113
114 #define ICE_SYSCTL_HELP_FWLOG_UART_ENA \
115 "\nControl whether to enable/disable reporting to UART" \
116 "\n0 - Enable firmware reporting via UART" \
117 "\n1 - Disable firmware reporting via UART"
118
119 #define ICE_SYSCTL_HELP_FWLOG_ENABLE_ON_LOAD \
120 "\nControl whether to enable logging during the attach phase" \
121 "\n0 - Enable firmware logging during attach phase" \
122 "\n1 - Disable firmware logging during attach phase"
123
124 #define ICE_SYSCTL_HELP_FWLOG_REGISTER \
125 "\nControl whether to enable/disable firmware logging" \
126 "\n0 - Enable firmware logging" \
127 "\n1 - Disable firmware logging"
128
129 #define ICE_SYSCTL_HELP_FWLOG_MODULE_SEVERITY \
130 "\nControl the level of log output messages for this module" \
131 "\n\tverbose <4> - Verbose messages + (Error|Warning|Normal)" \
132 "\n\tnormal <3> - Normal messages + (Error|Warning)" \
133 "\n\twarning <2> - Warning messages + (Error)" \
134 "\n\terror <1> - Error messages" \
135 "\n\tnone <0> - Disables all logging for this module"
136
137 /**
138 * ice_sysctl_fwlog_set_cfg_options - Sysctl for setting fwlog cfg options
139 * @oidp: sysctl oid structure
140 * @arg1: private softc structure
141 * @arg2: option to adjust
142 * @req: sysctl request pointer
143 *
144 * On read: displays whether firmware logging was reported during attachment
145 * On write: enables/disables firmware logging during attach phase
146 *
147 * This has no effect on the legacy (V1) version of firmware logging.
148 */
149 static int
ice_sysctl_fwlog_set_cfg_options(SYSCTL_HANDLER_ARGS)150 ice_sysctl_fwlog_set_cfg_options(SYSCTL_HANDLER_ARGS)
151 {
152 struct ice_softc *sc = (struct ice_softc *)arg1;
153 struct ice_fwlog_cfg *cfg = &sc->hw.fwlog_cfg;
154 int error;
155 u16 option = (u16)arg2;
156 bool enabled;
157
158 enabled = !!(cfg->options & option);
159
160 error = sysctl_handle_bool(oidp, &enabled, 0, req);
161 if ((error) || (req->newptr == NULL))
162 return (error);
163
164 if (enabled)
165 cfg->options |= option;
166 else
167 cfg->options &= ~option;
168
169 return ice_reconfig_fw_log(sc, cfg);
170 }
171
172 /**
173 * ice_sysctl_fwlog_log_resolution - Sysctl for setting log message resolution
174 * @oidp: sysctl oid structure
175 * @arg1: private softc structure
176 * @arg2: __unused__
177 * @req: sysctl request pointer
178 *
179 * On read: displays message queue limit before posting
180 * On write: sets message queue limit before posting
181 *
182 * This has no effect on the legacy (V1) version of firmware logging.
183 */
184 static int
ice_sysctl_fwlog_log_resolution(SYSCTL_HANDLER_ARGS)185 ice_sysctl_fwlog_log_resolution(SYSCTL_HANDLER_ARGS)
186 {
187 struct ice_softc *sc = (struct ice_softc *)arg1;
188 struct ice_fwlog_cfg *cfg = &sc->hw.fwlog_cfg;
189 int error;
190 u8 resolution;
191
192 UNREFERENCED_PARAMETER(arg2);
193
194 resolution = cfg->log_resolution;
195
196 error = sysctl_handle_8(oidp, &resolution, 0, req);
197 if ((error) || (req->newptr == NULL))
198 return (error);
199
200 if ((resolution < ICE_AQC_FW_LOG_MIN_RESOLUTION) ||
201 (resolution > ICE_AQC_FW_LOG_MAX_RESOLUTION)) {
202 device_printf(sc->dev, "Log resolution out-of-bounds\n");
203 return (EINVAL);
204 }
205
206 cfg->log_resolution = resolution;
207
208 return ice_reconfig_fw_log(sc, cfg);
209 }
210
211 /**
212 * ice_sysctl_fwlog_register - Sysctl for (de)registering firmware logs
213 * @oidp: sysctl oid structure
214 * @arg1: private softc structure
215 * @arg2: __unused__
216 * @req: sysctl request pointer
217 *
218 * On read: displays whether firmware logging is registered
219 * On write: (de)registers firmware logging.
220 */
221 static int
ice_sysctl_fwlog_register(SYSCTL_HANDLER_ARGS)222 ice_sysctl_fwlog_register(SYSCTL_HANDLER_ARGS)
223 {
224 struct ice_softc *sc = (struct ice_softc *)arg1;
225 struct ice_fwlog_cfg *cfg = &sc->hw.fwlog_cfg;
226 enum ice_status status;
227 int error;
228 u8 enabled;
229
230 UNREFERENCED_PARAMETER(arg2);
231
232 if (ice_test_state(&sc->state, ICE_STATE_ATTACHING)) {
233 device_printf(sc->dev, "Registering FW Logging via kenv is supported with the on_load option\n");
234 return (EIO);
235 }
236
237 if (cfg->options & ICE_FWLOG_OPTION_IS_REGISTERED)
238 enabled = true;
239 else
240 enabled = false;
241
242 error = sysctl_handle_bool(oidp, &enabled, 0, req);
243 if ((error) || (req->newptr == NULL))
244 return (error);
245
246 if (!ice_check_sq_alive(&sc->hw, &sc->hw.adminq))
247 return (0);
248
249 if (enabled) {
250 status = ice_fwlog_register(&sc->hw);
251 if (!status)
252 ice_set_bit(ICE_FEATURE_FW_LOGGING, sc->feat_en);
253 } else {
254 status = ice_fwlog_unregister(&sc->hw);
255 if (!status)
256 ice_clear_bit(ICE_FEATURE_FW_LOGGING, sc->feat_en);
257 }
258
259 if (status)
260 return (EIO);
261
262 return (0);
263 }
264
265 /**
266 * ice_sysctl_fwlog_module_log_severity - Add tunables for a FW logging module
267 * @oidp: sysctl oid structure
268 * @arg1: private softc structure
269 * @arg2: index to logging module
270 * @req: sysctl request pointer
271 */
272 static int
ice_sysctl_fwlog_module_log_severity(SYSCTL_HANDLER_ARGS)273 ice_sysctl_fwlog_module_log_severity(SYSCTL_HANDLER_ARGS)
274 {
275 struct ice_softc *sc = (struct ice_softc *)arg1;
276 struct ice_fwlog_cfg *cfg = &sc->hw.fwlog_cfg;
277 struct sbuf *sbuf;
278 char *sev_str_end;
279 enum ice_aqc_fw_logging_mod module = (enum ice_aqc_fw_logging_mod)arg2;
280 int error, ll_num;
281 u8 log_level;
282 char sev_str[16];
283 bool sev_set = false;
284
285 log_level = cfg->module_entries[module].log_level;
286 sbuf = sbuf_new(NULL, sev_str, sizeof(sev_str), SBUF_FIXEDLEN);
287 sbuf_printf(sbuf, "%d<%s>", log_level, ice_log_sev_str(log_level));
288 sbuf_finish(sbuf);
289 sbuf_delete(sbuf);
290
291 error = sysctl_handle_string(oidp, sev_str, sizeof(sev_str), req);
292 if ((error) || (req->newptr == NULL))
293 return (error);
294
295 if (strcasecmp(ice_log_sev_str(ICE_FWLOG_LEVEL_VERBOSE), sev_str) == 0) {
296 log_level = ICE_FWLOG_LEVEL_VERBOSE;
297 sev_set = true;
298 } else if (strcasecmp(ice_log_sev_str(ICE_FWLOG_LEVEL_NORMAL), sev_str) == 0) {
299 log_level = ICE_FWLOG_LEVEL_NORMAL;
300 sev_set = true;
301 } else if (strcasecmp(ice_log_sev_str(ICE_FWLOG_LEVEL_WARNING), sev_str) == 0) {
302 log_level = ICE_FWLOG_LEVEL_WARNING;
303 sev_set = true;
304 } else if (strcasecmp(ice_log_sev_str(ICE_FWLOG_LEVEL_ERROR), sev_str) == 0) {
305 log_level = ICE_FWLOG_LEVEL_ERROR;
306 sev_set = true;
307 } else if (strcasecmp(ice_log_sev_str(ICE_FWLOG_LEVEL_NONE), sev_str) == 0) {
308 log_level = ICE_FWLOG_LEVEL_NONE;
309 sev_set = true;
310 }
311
312 if (!sev_set) {
313 ll_num = strtol(sev_str, &sev_str_end, 0);
314 if (sev_str_end == sev_str)
315 ll_num = -1;
316 if ((ll_num >= ICE_FWLOG_LEVEL_NONE) &&
317 (ll_num < ICE_FWLOG_LEVEL_INVALID))
318 log_level = ll_num;
319 else {
320 device_printf(sc->dev,
321 "%s: \"%s\" is not a valid log level\n",
322 __func__, sev_str);
323 return (EINVAL);
324 }
325 }
326
327 cfg->module_entries[module].log_level = log_level;
328
329 return ice_reconfig_fw_log(sc, cfg);
330 }
331
332 /**
333 * ice_add_fw_logging_tunables - Add tunables to configure FW logging events
334 * @sc: private softc structure
335 * @parent: parent node to add the tunables under
336 *
337 * Add tunables for configuring the firmware logging support. This includes
338 * a control to enable the logging, and controls for each module to configure
339 * which events to receive.
340 */
341 void
ice_add_fw_logging_tunables(struct ice_softc * sc,struct sysctl_oid * parent)342 ice_add_fw_logging_tunables(struct ice_softc *sc, struct sysctl_oid *parent)
343 {
344 struct sysctl_oid_list *parent_list, *fwlog_list, *module_list;
345 struct sysctl_oid *fwlog_node, *module_node;
346 struct sysctl_ctx_list *ctx;
347 struct ice_hw *hw = &sc->hw;
348 struct ice_fwlog_cfg *cfg;
349 device_t dev = sc->dev;
350 enum ice_aqc_fw_logging_mod module;
351 u16 i;
352
353 cfg = &hw->fwlog_cfg;
354 ctx = device_get_sysctl_ctx(dev);
355 parent_list = SYSCTL_CHILDREN(parent);
356
357 fwlog_node = SYSCTL_ADD_NODE(ctx, parent_list, OID_AUTO, "fw_log",
358 ICE_CTLFLAG_DEBUG | CTLFLAG_RD, NULL,
359 "Firmware Logging");
360 fwlog_list = SYSCTL_CHILDREN(fwlog_node);
361
362 cfg->log_resolution = 10;
363 SYSCTL_ADD_PROC(ctx, fwlog_list, OID_AUTO, "log_resolution",
364 ICE_CTLFLAG_DEBUG | CTLTYPE_U8 | CTLFLAG_RWTUN, sc,
365 0, ice_sysctl_fwlog_log_resolution,
366 "CU", ICE_SYSCTL_HELP_FWLOG_LOG_RESOLUTION);
367
368 cfg->options |= ICE_FWLOG_OPTION_ARQ_ENA;
369 SYSCTL_ADD_PROC(ctx, fwlog_list, OID_AUTO, "arq_en",
370 ICE_CTLFLAG_DEBUG | CTLTYPE_U8 | CTLFLAG_RWTUN, sc,
371 ICE_FWLOG_OPTION_ARQ_ENA, ice_sysctl_fwlog_set_cfg_options,
372 "CU", ICE_SYSCTL_HELP_FWLOG_ARQ_ENA);
373
374 SYSCTL_ADD_PROC(ctx, fwlog_list, OID_AUTO, "uart_en",
375 ICE_CTLFLAG_DEBUG | CTLTYPE_U8 | CTLFLAG_RWTUN, sc,
376 ICE_FWLOG_OPTION_UART_ENA, ice_sysctl_fwlog_set_cfg_options,
377 "CU", ICE_SYSCTL_HELP_FWLOG_UART_ENA);
378
379 SYSCTL_ADD_PROC(ctx, fwlog_list, OID_AUTO, "on_load",
380 ICE_CTLFLAG_DEBUG | CTLTYPE_U8 | CTLFLAG_RWTUN, sc,
381 ICE_FWLOG_OPTION_REGISTER_ON_INIT, ice_sysctl_fwlog_set_cfg_options,
382 "CU", ICE_SYSCTL_HELP_FWLOG_ENABLE_ON_LOAD);
383
384 SYSCTL_ADD_PROC(ctx, fwlog_list, OID_AUTO, "register",
385 ICE_CTLFLAG_DEBUG | CTLTYPE_U8 | CTLFLAG_RWTUN, sc,
386 0, ice_sysctl_fwlog_register,
387 "CU", ICE_SYSCTL_HELP_FWLOG_REGISTER);
388
389 module_node = SYSCTL_ADD_NODE(ctx, fwlog_list, OID_AUTO, "severity",
390 ICE_CTLFLAG_DEBUG | CTLFLAG_RD, NULL,
391 "Level of log output");
392
393 module_list = SYSCTL_CHILDREN(module_node);
394
395 for (i = 0; i < ICE_AQC_FW_LOG_ID_MAX; i++) {
396 /* Setup some defaults */
397 cfg->module_entries[i].module_id = i;
398 cfg->module_entries[i].log_level = ICE_FWLOG_LEVEL_NONE;
399 module = (enum ice_aqc_fw_logging_mod)i;
400
401 SYSCTL_ADD_PROC(ctx, module_list,
402 OID_AUTO, ice_fw_module_str(module),
403 ICE_CTLFLAG_DEBUG | CTLTYPE_STRING | CTLFLAG_RWTUN, sc,
404 module, ice_sysctl_fwlog_module_log_severity,
405 "A", ICE_SYSCTL_HELP_FWLOG_MODULE_SEVERITY);
406 }
407 }
408
409 /**
410 * ice_handle_fw_log_event - Handle a firmware logging event from the AdminQ
411 * @sc: pointer to private softc structure
412 * @desc: the AdminQ descriptor for this firmware event
413 * @buf: pointer to the buffer accompanying the AQ message
414 */
415 void
ice_handle_fw_log_event(struct ice_softc * sc,struct ice_aq_desc * desc,void * buf)416 ice_handle_fw_log_event(struct ice_softc *sc, struct ice_aq_desc *desc,
417 void *buf)
418 {
419 /* Trigger a DTrace probe event for this firmware message */
420 SDT_PROBE2(ice_fwlog, , , message, (const u8 *)buf, desc->datalen);
421
422 /* Possibly dump the firmware message to the console, if enabled */
423 ice_fwlog_event_dump(&sc->hw, desc, buf);
424 }
425