xref: /freebsd/sys/dev/ice/ice_fwlog.c (revision 6580f5c38dd5b01aeeaed16b370f1a12423437f0)
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  */
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
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  */
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
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
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  */
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
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
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
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  */
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  */
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  */
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
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  */
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
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
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