1 /* SPDX-License-Identifier: BSD-3-Clause */ 2 /* Copyright (c) 2022, 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