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 #include "ice_common.h"
33 #include "ice_fwlog.h"
34
35 /**
36 * cache_cfg - Cache FW logging config
37 * @hw: pointer to the HW structure
38 * @cfg: config to cache
39 */
cache_cfg(struct ice_hw * hw,struct ice_fwlog_cfg * cfg)40 static void cache_cfg(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
41 {
42 hw->fwlog_cfg = *cfg;
43 }
44
45 /**
46 * valid_module_entries - validate all the module entry IDs and log levels
47 * @hw: pointer to the HW structure
48 * @entries: entries to validate
49 * @num_entries: number of entries to validate
50 */
51 static bool
valid_module_entries(struct ice_hw * hw,struct ice_fwlog_module_entry * entries,u16 num_entries)52 valid_module_entries(struct ice_hw *hw, struct ice_fwlog_module_entry *entries,
53 u16 num_entries)
54 {
55 u16 i;
56
57 if (!entries) {
58 ice_debug(hw, ICE_DBG_FW_LOG, "Null ice_fwlog_module_entry array\n");
59 return false;
60 }
61
62 if (!num_entries) {
63 ice_debug(hw, ICE_DBG_FW_LOG, "num_entries must be non-zero\n");
64 return false;
65 }
66
67 for (i = 0; i < num_entries; i++) {
68 struct ice_fwlog_module_entry *entry = &entries[i];
69
70 if (entry->module_id >= ICE_AQC_FW_LOG_ID_MAX) {
71 ice_debug(hw, ICE_DBG_FW_LOG, "Invalid module_id %u, max valid module_id is %u\n",
72 entry->module_id, ICE_AQC_FW_LOG_ID_MAX - 1);
73 return false;
74 }
75
76 if (entry->log_level >= ICE_FWLOG_LEVEL_INVALID) {
77 ice_debug(hw, ICE_DBG_FW_LOG, "Invalid log_level %u, max valid log_level is %u\n",
78 entry->log_level,
79 ICE_AQC_FW_LOG_ID_MAX - 1);
80 return false;
81 }
82 }
83
84 return true;
85 }
86
87 /**
88 * valid_cfg - validate entire configuration
89 * @hw: pointer to the HW structure
90 * @cfg: config to validate
91 */
valid_cfg(struct ice_hw * hw,struct ice_fwlog_cfg * cfg)92 static bool valid_cfg(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
93 {
94 if (!cfg) {
95 ice_debug(hw, ICE_DBG_FW_LOG, "Null ice_fwlog_cfg\n");
96 return false;
97 }
98
99 if (cfg->log_resolution < ICE_AQC_FW_LOG_MIN_RESOLUTION ||
100 cfg->log_resolution > ICE_AQC_FW_LOG_MAX_RESOLUTION) {
101 ice_debug(hw, ICE_DBG_FW_LOG, "Unsupported log_resolution %u, must be between %u and %u\n",
102 cfg->log_resolution, ICE_AQC_FW_LOG_MIN_RESOLUTION,
103 ICE_AQC_FW_LOG_MAX_RESOLUTION);
104 return false;
105 }
106
107 if (!valid_module_entries(hw, cfg->module_entries,
108 ICE_AQC_FW_LOG_ID_MAX))
109 return false;
110
111 return true;
112 }
113
114 /**
115 * ice_fwlog_init - Initialize cached structures for tracking FW logging
116 * @hw: pointer to the HW structure
117 * @cfg: config used to initialize the cached structures
118 *
119 * This function should be called on driver initialization and before calling
120 * ice_init_hw(). Firmware logging will be configured based on these settings
121 * and also the PF will be registered on init.
122 */
123 enum ice_status
ice_fwlog_init(struct ice_hw * hw,struct ice_fwlog_cfg * cfg)124 ice_fwlog_init(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
125 {
126 if (!valid_cfg(hw, cfg))
127 return ICE_ERR_PARAM;
128
129 cache_cfg(hw, cfg);
130
131 return ICE_SUCCESS;
132 }
133
134 /**
135 * ice_aq_fwlog_set - Set FW logging configuration AQ command (0xFF30)
136 * @hw: pointer to the HW structure
137 * @entries: entries to configure
138 * @num_entries: number of @entries
139 * @options: options from ice_fwlog_cfg->options structure
140 * @log_resolution: logging resolution
141 */
142 static enum ice_status
ice_aq_fwlog_set(struct ice_hw * hw,struct ice_fwlog_module_entry * entries,u16 num_entries,u16 options,u16 log_resolution)143 ice_aq_fwlog_set(struct ice_hw *hw, struct ice_fwlog_module_entry *entries,
144 u16 num_entries, u16 options, u16 log_resolution)
145 {
146 struct ice_aqc_fw_log_cfg_resp *fw_modules;
147 struct ice_aqc_fw_log *cmd;
148 struct ice_aq_desc desc;
149 enum ice_status status;
150 u16 i;
151
152 fw_modules = (struct ice_aqc_fw_log_cfg_resp *)
153 ice_calloc(hw, num_entries, sizeof(*fw_modules));
154 if (!fw_modules)
155 return ICE_ERR_NO_MEMORY;
156
157 for (i = 0; i < num_entries; i++) {
158 fw_modules[i].module_identifier =
159 CPU_TO_LE16(entries[i].module_id);
160 fw_modules[i].log_level = entries[i].log_level;
161 }
162
163 ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_fw_logs_config);
164 desc.flags |= CPU_TO_LE16(ICE_AQ_FLAG_RD);
165
166 cmd = &desc.params.fw_log;
167
168 cmd->cmd_flags = ICE_AQC_FW_LOG_CONF_SET_VALID;
169 cmd->ops.cfg.log_resolution = CPU_TO_LE16(log_resolution);
170 cmd->ops.cfg.mdl_cnt = CPU_TO_LE16(num_entries);
171
172 if (options & ICE_FWLOG_OPTION_ARQ_ENA)
173 cmd->cmd_flags |= ICE_AQC_FW_LOG_CONF_AQ_EN;
174 if (options & ICE_FWLOG_OPTION_UART_ENA)
175 cmd->cmd_flags |= ICE_AQC_FW_LOG_CONF_UART_EN;
176
177 status = ice_aq_send_cmd(hw, &desc, fw_modules,
178 sizeof(*fw_modules) * num_entries,
179 NULL);
180
181 ice_free(hw, fw_modules);
182
183 return status;
184 }
185
186 /**
187 * ice_fwlog_supported - Cached for whether FW supports FW logging or not
188 * @hw: pointer to the HW structure
189 *
190 * This will always return false if called before ice_init_hw(), so it must be
191 * called after ice_init_hw().
192 */
ice_fwlog_supported(struct ice_hw * hw)193 bool ice_fwlog_supported(struct ice_hw *hw)
194 {
195 return hw->fwlog_support_ena;
196 }
197
198 /**
199 * ice_fwlog_set - Set the firmware logging settings
200 * @hw: pointer to the HW structure
201 * @cfg: config used to set firmware logging
202 *
203 * This function should be called whenever the driver needs to set the firmware
204 * logging configuration. It can be called on initialization, reset, or during
205 * runtime.
206 *
207 * If the PF wishes to receive FW logging then it must register via
208 * ice_fwlog_register. Note, that ice_fwlog_register does not need to be called
209 * for init.
210 */
211 enum ice_status
ice_fwlog_set(struct ice_hw * hw,struct ice_fwlog_cfg * cfg)212 ice_fwlog_set(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
213 {
214 enum ice_status status;
215
216 if (!ice_fwlog_supported(hw))
217 return ICE_ERR_NOT_SUPPORTED;
218
219 if (!valid_cfg(hw, cfg))
220 return ICE_ERR_PARAM;
221
222 status = ice_aq_fwlog_set(hw, cfg->module_entries,
223 ICE_AQC_FW_LOG_ID_MAX, cfg->options,
224 cfg->log_resolution);
225 if (!status)
226 cache_cfg(hw, cfg);
227
228 return status;
229 }
230
231 /**
232 * update_cached_entries - Update module entries in cached FW logging config
233 * @hw: pointer to the HW structure
234 * @entries: entries to cache
235 * @num_entries: number of @entries
236 */
237 static void
update_cached_entries(struct ice_hw * hw,struct ice_fwlog_module_entry * entries,u16 num_entries)238 update_cached_entries(struct ice_hw *hw, struct ice_fwlog_module_entry *entries,
239 u16 num_entries)
240 {
241 u16 i;
242
243 for (i = 0; i < num_entries; i++) {
244 struct ice_fwlog_module_entry *updated = &entries[i];
245 u16 j;
246
247 for (j = 0; j < ICE_AQC_FW_LOG_ID_MAX; j++) {
248 struct ice_fwlog_module_entry *cached =
249 &hw->fwlog_cfg.module_entries[j];
250
251 if (cached->module_id == updated->module_id) {
252 cached->log_level = updated->log_level;
253 break;
254 }
255 }
256 }
257 }
258
259 /**
260 * ice_fwlog_update_modules - Update the log level 1 or more FW logging modules
261 * @hw: pointer to the HW structure
262 * @entries: array of ice_fwlog_module_entry(s)
263 * @num_entries: number of entries
264 *
265 * This function should be called to update the log level of 1 or more FW
266 * logging modules via module ID.
267 *
268 * Only the entries passed in will be affected. All other firmware logging
269 * settings will be unaffected.
270 */
271 enum ice_status
ice_fwlog_update_modules(struct ice_hw * hw,struct ice_fwlog_module_entry * entries,u16 num_entries)272 ice_fwlog_update_modules(struct ice_hw *hw,
273 struct ice_fwlog_module_entry *entries,
274 u16 num_entries)
275 {
276 struct ice_fwlog_cfg *cfg;
277 enum ice_status status;
278
279 if (!ice_fwlog_supported(hw))
280 return ICE_ERR_NOT_SUPPORTED;
281
282 if (!valid_module_entries(hw, entries, num_entries))
283 return ICE_ERR_PARAM;
284
285 cfg = (struct ice_fwlog_cfg *)ice_calloc(hw, 1, sizeof(*cfg));
286 if (!cfg)
287 return ICE_ERR_NO_MEMORY;
288
289 status = ice_fwlog_get(hw, cfg);
290 if (status)
291 goto status_out;
292
293 status = ice_aq_fwlog_set(hw, entries, num_entries, cfg->options,
294 cfg->log_resolution);
295 if (!status)
296 update_cached_entries(hw, entries, num_entries);
297
298 status_out:
299 ice_free(hw, cfg);
300 return status;
301 }
302
303 /**
304 * ice_aq_fwlog_register - Register PF for firmware logging events (0xFF31)
305 * @hw: pointer to the HW structure
306 * @reg: true to register and false to unregister
307 */
ice_aq_fwlog_register(struct ice_hw * hw,bool reg)308 static enum ice_status ice_aq_fwlog_register(struct ice_hw *hw, bool reg)
309 {
310 struct ice_aq_desc desc;
311
312 ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_fw_logs_register);
313
314 if (reg)
315 desc.params.fw_log.cmd_flags = ICE_AQC_FW_LOG_AQ_REGISTER;
316
317 return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
318 }
319
320 /**
321 * ice_fwlog_register - Register the PF for firmware logging
322 * @hw: pointer to the HW structure
323 *
324 * After this call the PF will start to receive firmware logging based on the
325 * configuration set in ice_fwlog_set.
326 */
ice_fwlog_register(struct ice_hw * hw)327 enum ice_status ice_fwlog_register(struct ice_hw *hw)
328 {
329 enum ice_status status;
330
331 if (!ice_fwlog_supported(hw))
332 return ICE_ERR_NOT_SUPPORTED;
333
334 status = ice_aq_fwlog_register(hw, true);
335 if (status)
336 ice_debug(hw, ICE_DBG_FW_LOG, "Failed to register for firmware logging events over ARQ\n");
337 else
338 hw->fwlog_cfg.options |= ICE_FWLOG_OPTION_IS_REGISTERED;
339
340 return status;
341 }
342
343 /**
344 * ice_fwlog_unregister - Unregister the PF from firmware logging
345 * @hw: pointer to the HW structure
346 */
ice_fwlog_unregister(struct ice_hw * hw)347 enum ice_status ice_fwlog_unregister(struct ice_hw *hw)
348 {
349 enum ice_status status;
350
351 if (!ice_fwlog_supported(hw))
352 return ICE_ERR_NOT_SUPPORTED;
353
354 status = ice_aq_fwlog_register(hw, false);
355 if (status)
356 ice_debug(hw, ICE_DBG_FW_LOG, "Failed to unregister from firmware logging events over ARQ\n");
357 else
358 hw->fwlog_cfg.options &= ~ICE_FWLOG_OPTION_IS_REGISTERED;
359
360 return status;
361 }
362
363 /**
364 * ice_aq_fwlog_get - Get the current firmware logging configuration (0xFF32)
365 * @hw: pointer to the HW structure
366 * @cfg: firmware logging configuration to populate
367 */
368 static enum ice_status
ice_aq_fwlog_get(struct ice_hw * hw,struct ice_fwlog_cfg * cfg)369 ice_aq_fwlog_get(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
370 {
371 struct ice_aqc_fw_log_cfg_resp *fw_modules;
372 struct ice_aqc_fw_log *cmd;
373 struct ice_aq_desc desc;
374 enum ice_status status;
375 u16 i, module_id_cnt;
376 void *buf;
377
378 ice_memset(cfg, 0, sizeof(*cfg), ICE_NONDMA_MEM);
379
380 buf = ice_calloc(hw, 1, ICE_AQ_MAX_BUF_LEN);
381 if (!buf)
382 return ICE_ERR_NO_MEMORY;
383
384 ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_fw_logs_query);
385 cmd = &desc.params.fw_log;
386
387 cmd->cmd_flags = ICE_AQC_FW_LOG_AQ_QUERY;
388
389 status = ice_aq_send_cmd(hw, &desc, buf, ICE_AQ_MAX_BUF_LEN, NULL);
390 if (status) {
391 ice_debug(hw, ICE_DBG_FW_LOG, "Failed to get FW log configuration\n");
392 goto status_out;
393 }
394
395 module_id_cnt = LE16_TO_CPU(cmd->ops.cfg.mdl_cnt);
396 if (module_id_cnt < ICE_AQC_FW_LOG_ID_MAX) {
397 ice_debug(hw, ICE_DBG_FW_LOG, "FW returned less than the expected number of FW log module IDs\n");
398 } else {
399 if (module_id_cnt > ICE_AQC_FW_LOG_ID_MAX)
400 ice_debug(hw, ICE_DBG_FW_LOG, "FW returned more than expected number of FW log module IDs, setting module_id_cnt to software expected max %u\n",
401 ICE_AQC_FW_LOG_ID_MAX);
402 module_id_cnt = ICE_AQC_FW_LOG_ID_MAX;
403 }
404
405 cfg->log_resolution = LE16_TO_CPU(cmd->ops.cfg.log_resolution);
406 if (cmd->cmd_flags & ICE_AQC_FW_LOG_CONF_AQ_EN)
407 cfg->options |= ICE_FWLOG_OPTION_ARQ_ENA;
408 if (cmd->cmd_flags & ICE_AQC_FW_LOG_CONF_UART_EN)
409 cfg->options |= ICE_FWLOG_OPTION_UART_ENA;
410 if (cmd->cmd_flags & ICE_AQC_FW_LOG_QUERY_REGISTERED)
411 cfg->options |= ICE_FWLOG_OPTION_IS_REGISTERED;
412
413 fw_modules = (struct ice_aqc_fw_log_cfg_resp *)buf;
414
415 for (i = 0; i < module_id_cnt; i++) {
416 struct ice_aqc_fw_log_cfg_resp *fw_module = &fw_modules[i];
417
418 cfg->module_entries[i].module_id =
419 LE16_TO_CPU(fw_module->module_identifier);
420 cfg->module_entries[i].log_level = fw_module->log_level;
421 }
422
423 status_out:
424 ice_free(hw, buf);
425 return status;
426 }
427
428 /**
429 * ice_fwlog_set_support_ena - Set if FW logging is supported by FW
430 * @hw: pointer to the HW struct
431 *
432 * If FW returns success to the ice_aq_fwlog_get call then it supports FW
433 * logging, else it doesn't. Set the fwlog_support_ena flag accordingly.
434 *
435 * This function is only meant to be called during driver init to determine if
436 * the FW support FW logging.
437 */
ice_fwlog_set_support_ena(struct ice_hw * hw)438 void ice_fwlog_set_support_ena(struct ice_hw *hw)
439 {
440 struct ice_fwlog_cfg *cfg;
441 enum ice_status status;
442
443 hw->fwlog_support_ena = false;
444
445 cfg = (struct ice_fwlog_cfg *)ice_calloc(hw, 1, sizeof(*cfg));
446 if (!cfg)
447 return;
448
449 /* don't call ice_fwlog_get() because that would overwrite the cached
450 * configuration from the call to ice_fwlog_init(), which is expected to
451 * be called prior to this function
452 */
453 status = ice_aq_fwlog_get(hw, cfg);
454 if (status)
455 ice_debug(hw, ICE_DBG_FW_LOG, "ice_fwlog_get failed, FW logging is not supported on this version of FW, status %d\n",
456 status);
457 else
458 hw->fwlog_support_ena = true;
459
460 ice_free(hw, cfg);
461 }
462
463 /**
464 * ice_fwlog_get - Get the firmware logging settings
465 * @hw: pointer to the HW structure
466 * @cfg: config to populate based on current firmware logging settings
467 */
468 enum ice_status
ice_fwlog_get(struct ice_hw * hw,struct ice_fwlog_cfg * cfg)469 ice_fwlog_get(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
470 {
471 enum ice_status status;
472
473 if (!ice_fwlog_supported(hw))
474 return ICE_ERR_NOT_SUPPORTED;
475
476 if (!cfg)
477 return ICE_ERR_PARAM;
478
479 status = ice_aq_fwlog_get(hw, cfg);
480 if (status)
481 return status;
482
483 cache_cfg(hw, cfg);
484
485 return ICE_SUCCESS;
486 }
487
488 /**
489 * ice_fwlog_event_dump - Dump the event received over the Admin Receive Queue
490 * @hw: pointer to the HW structure
491 * @desc: Admin Receive Queue descriptor
492 * @buf: buffer that contains the FW log event data
493 *
494 * If the driver receives the ice_aqc_opc_fw_logs_event on the Admin Receive
495 * Queue, then it should call this function to dump the FW log data.
496 */
497 void
ice_fwlog_event_dump(struct ice_hw * hw,struct ice_aq_desc * desc,void * buf)498 ice_fwlog_event_dump(struct ice_hw *hw, struct ice_aq_desc *desc, void *buf)
499 {
500 if (!ice_fwlog_supported(hw))
501 return;
502
503 ice_info_fwlog(hw, 32, 1, (u8 *)buf, LE16_TO_CPU(desc->datalen));
504 }
505
506