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 */
40static 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 */
51static bool
52valid_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 */
92static 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 */
123enum ice_status
124ice_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 */
142static enum ice_status
143ice_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 */
193bool 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 */
211enum ice_status
212ice_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 */
237static void
238update_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 */
271enum ice_status
272ice_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
298status_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 */
308static 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 */
327enum 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 */
347enum 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 */
368static enum ice_status
369ice_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
423status_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 */
438void 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 */
468enum ice_status
469ice_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 */
497void
498ice_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