xref: /freebsd/sys/dev/ice/ice_fw_logging.c (revision 72ae04c7334743c0fec2e8c99ac13fd8ca63f51b)
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
80 ice_reconfig_fw_log(struct ice_softc *sc, struct ice_fwlog_cfg *cfg)
81 {
82 	int 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
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
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
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 	int 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
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
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 	SYSCTL_ADD_PROC(ctx, fwlog_list, OID_AUTO, "on_load",
363 	    ICE_CTLFLAG_DEBUG | CTLTYPE_U8 | CTLFLAG_RWTUN, sc,
364 	    ICE_FWLOG_OPTION_REGISTER_ON_INIT, ice_sysctl_fwlog_set_cfg_options,
365 	    "CU", ICE_SYSCTL_HELP_FWLOG_ENABLE_ON_LOAD);
366 
367 	SYSCTL_ADD_PROC(ctx, fwlog_list, OID_AUTO, "register",
368 	    ICE_CTLFLAG_DEBUG | CTLTYPE_U8 | CTLFLAG_RWTUN, sc,
369 	    0, ice_sysctl_fwlog_register,
370 	    "CU", ICE_SYSCTL_HELP_FWLOG_REGISTER);
371 
372 	hw->pf_id = ice_get_pf_id(hw);
373 	if (hw->pf_id == 0) {
374 		module_node = SYSCTL_ADD_NODE(ctx, fwlog_list, OID_AUTO, "severity",
375 					      ICE_CTLFLAG_DEBUG | CTLFLAG_RD, NULL,
376 					      "Level of log output");
377 
378 		module_list = SYSCTL_CHILDREN(module_node);
379 
380 		for (i = 0; i < ICE_AQC_FW_LOG_ID_MAX; i++) {
381 			/* Setup some defaults */
382 			cfg->module_entries[i].module_id = i;
383 			cfg->module_entries[i].log_level = ICE_FWLOG_LEVEL_NONE;
384 			module = (enum ice_aqc_fw_logging_mod)i;
385 
386 			SYSCTL_ADD_PROC(ctx, module_list,
387 			    OID_AUTO, ice_fw_module_str(module),
388 			    ICE_CTLFLAG_DEBUG | CTLTYPE_STRING | CTLFLAG_RWTUN, sc,
389 			    module, ice_sysctl_fwlog_module_log_severity,
390 			    "A", ICE_SYSCTL_HELP_FWLOG_MODULE_SEVERITY);
391 		}
392 
393 		cfg->log_resolution = 10;
394 		SYSCTL_ADD_PROC(ctx, fwlog_list, OID_AUTO, "log_resolution",
395 		    ICE_CTLFLAG_DEBUG | CTLTYPE_U8 | CTLFLAG_RWTUN, sc,
396 		    0, ice_sysctl_fwlog_log_resolution,
397 		    "CU", ICE_SYSCTL_HELP_FWLOG_LOG_RESOLUTION);
398 
399 		cfg->options |= ICE_FWLOG_OPTION_ARQ_ENA;
400 		SYSCTL_ADD_PROC(ctx, fwlog_list, OID_AUTO, "arq_en",
401 		    ICE_CTLFLAG_DEBUG | CTLTYPE_U8 | CTLFLAG_RWTUN, sc,
402 		    ICE_FWLOG_OPTION_ARQ_ENA, ice_sysctl_fwlog_set_cfg_options,
403 		    "CU", ICE_SYSCTL_HELP_FWLOG_ARQ_ENA);
404 
405 		SYSCTL_ADD_PROC(ctx, fwlog_list, OID_AUTO, "uart_en",
406 		    ICE_CTLFLAG_DEBUG | CTLTYPE_U8 | CTLFLAG_RWTUN, sc,
407 		    ICE_FWLOG_OPTION_UART_ENA, ice_sysctl_fwlog_set_cfg_options,
408 		    "CU", ICE_SYSCTL_HELP_FWLOG_UART_ENA);
409 	}
410 }
411 
412 /**
413  * ice_handle_fw_log_event - Handle a firmware logging event from the AdminQ
414  * @sc: pointer to private softc structure
415  * @desc: the AdminQ descriptor for this firmware event
416  * @buf: pointer to the buffer accompanying the AQ message
417  */
418 void
419 ice_handle_fw_log_event(struct ice_softc *sc, struct ice_aq_desc *desc,
420 			void *buf)
421 {
422 	/* Trigger a DTrace probe event for this firmware message */
423 	SDT_PROBE2(ice_fwlog, , , message, (const u8 *)buf, desc->datalen);
424 
425 	/* Possibly dump the firmware message to the console, if enabled */
426 	ice_fwlog_event_dump(&sc->hw, desc, buf);
427 }
428