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 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 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 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 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 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 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