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