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