• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Firmware trace handling on the DHD side. Kernel thread reads the trace data and writes
3  * to the file and implements various utility functions.
4  *
5  * Broadcom Proprietary and Confidential. Copyright (C) 2020,
6  * All Rights Reserved.
7  *
8  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom;
9  * the contents of this file may not be disclosed to third parties,
10  * copied or duplicated in any form, in whole or in part, without
11  * the prior written permission of Broadcom.
12  *
13  *
14  * <<Broadcom-WL-IPTag/Proprietary:>>
15  *
16  * $Id$
17  */
18 
19 #ifdef BCMINTERNAL
20 
21 #ifdef DHD_FWTRACE
22 
23 #include <typedefs.h>
24 #include <osl.h>
25 
26 #include <bcmutils.h>
27 #include <bcmendian.h>
28 
29 #include <dngl_stats.h>
30 #include <dhd.h>
31 #include <dhd_proto.h>
32 #include <dhd_dbg.h>
33 #include <dhd_debug.h>
34 
35 #include <dhd_fwtrace.h>
36 
37 static int fwtrace_write_to_file(uint8 *buf, uint16 buf_len, dhd_pub_t *dhdp);
38 static int fwtrace_close_file(dhd_pub_t *dhdp);
39 static int fwtrace_open_file(uint32 fw_trace_enabled, dhd_pub_t *dhdp);
40 static fwtrace_buf_t *fwtrace_get_trace_data_ptr(dhd_pub_t *dhdp);
41 static void fwtrace_free_trace_buf(dhd_pub_t *dhdp);
42 
43 typedef struct fwtrace_info {
44 	struct file *fw_trace_fp;
45 	int file_index;
46 	int part_index;
47 	int trace_buf_index;
48 	int trace_buf_count;
49 	uint16 overflow_counter;
50 
51 	char trace_file[TRACE_FILE_NAME_LEN];
52 
53 	fwtrace_buf_t *trace_data_ptr;
54 
55 	uint16 prev_seq;
56 
57 	uint32 fwtrace_enable;		/* Enable firmware tracing and the
58 					 * trace file management.
59 					 */
60 	struct mutex fwtrace_lock;	/* Synchronization between the
61 					 * ioctl and the kernel thread.
62 					 */
63 	dhd_dma_buf_t fwtrace_buf;	/* firmware trace buffer */
64 } fwtrace_info_t;
65 
66 int
dhd_fwtrace_attach(dhd_pub_t * dhdp)67 dhd_fwtrace_attach(dhd_pub_t *dhdp)
68 {
69 	fwtrace_info_t *fwtrace_info;
70 
71 	/* Allocate prot structure */
72 	if (!(fwtrace_info = (fwtrace_info_t *)VMALLOCZ(dhdp->osh, sizeof(*fwtrace_info)))) {
73 		DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
74 		return (BCME_NOMEM);
75 	}
76 
77 	bzero(fwtrace_info, sizeof(*fwtrace_info));
78 	dhdp->fwtrace_info = fwtrace_info;
79 
80 	mutex_init(&dhdp->fwtrace_info->fwtrace_lock);
81 
82 	DHD_INFO(("allocated DHD fwtrace\n"));
83 
84 	return BCME_OK;
85 }
86 
87 int
dhd_fwtrace_detach(dhd_pub_t * dhdp)88 dhd_fwtrace_detach(dhd_pub_t *dhdp)
89 {
90 	fwtrace_info_t *fwtrace_info;
91 
92 	DHD_TRACE(("%s: %d\n", __FUNCTION__, __LINE__));
93 
94 	if (!dhdp) {
95 		return BCME_OK;
96 	}
97 
98 	if (!dhdp->fwtrace_info) {
99 		return BCME_OK;
100 	}
101 
102 	fwtrace_info = dhdp->fwtrace_info;
103 
104 	dhd_dma_buf_free(dhdp, &dhdp->fwtrace_info->fwtrace_buf);
105 
106 	/* close the file if valid */
107 	if (!(IS_ERR_OR_NULL(dhdp->fwtrace_info->fw_trace_fp))) {
108 		(void) filp_close(dhdp->fwtrace_info->fw_trace_fp, 0);
109 	}
110 
111 	mutex_destroy(&dhdp->fwtrace_info->fwtrace_lock);
112 
113 	VMFREE(dhdp->osh, fwtrace_info, sizeof(*fwtrace_info));
114 
115 	dhdp->fwtrace_info = NULL;
116 
117 	DHD_INFO(("Deallocated DHD fwtrace_info\n"));
118 
119 	return (BCME_OK);
120 }
121 
122 uint16
get_fw_trace_overflow_counter(dhd_pub_t * dhdp)123 get_fw_trace_overflow_counter(dhd_pub_t *dhdp)
124 {
125 	return (dhdp->fwtrace_info->overflow_counter);
126 }
127 
128 void
process_fw_trace_data(dhd_pub_t * dhdp)129 process_fw_trace_data(dhd_pub_t *dhdp)
130 {
131 	fwtrace_info_t *fwtrace_info = dhdp->fwtrace_info;
132 	uint16 length;
133 	uint16 incoming_seq;
134 	uint32 trace_buf_index = fwtrace_info->trace_buf_index;
135 	fwtrace_buf_t * trace_buf;
136 	fwtrace_buf_t * curr_buf;
137 
138 	mutex_lock(&fwtrace_info->fwtrace_lock);
139 
140 	if (fwtrace_info->fw_trace_fp == NULL) {
141 		goto done;
142 	}
143 
144 	if ((trace_buf = fwtrace_get_trace_data_ptr(dhdp)) == NULL) {
145 		goto done;
146 	}
147 
148 	do {
149 		curr_buf = trace_buf + trace_buf_index;
150 
151 		length = curr_buf->info.length;
152 		/* If the incoming length is 0, means nothing is updated by the firmware */
153 		if (length == 0) {
154 			break;
155 		}
156 
157 		incoming_seq = curr_buf->info.seq_num;
158 
159 		if (((uint16)(fwtrace_info->prev_seq + 1) != incoming_seq) &&
160 			length != sizeof(*curr_buf)) {
161 			DHD_ERROR(("*** invalid trace len idx = %u, length = %u, "
162 				"cur seq = %u, in-seq = %u \n",
163 				trace_buf_index, length,
164 				fwtrace_info->prev_seq, incoming_seq));
165 			break;
166 		}
167 
168 		DHD_TRACE(("*** TRACE BUS: IDX:%d, in-seq:%d(prev-%d), ptr:%p(%llu), len:%d\n",
169 				trace_buf_index, incoming_seq, fwtrace_info->prev_seq,
170 				curr_buf, (uint64)curr_buf, length));
171 
172 		/* Write trace data to a file */
173 		if (fwtrace_write_to_file((uint8 *) curr_buf, length, dhdp) != BCME_OK) {
174 			DHD_ERROR(("*** fwtrace_write_to_file has failed \n"));
175 			break;
176 		}
177 
178 		/* Reset length after consuming the fwtrace data */
179 		curr_buf->info.length = 0;
180 
181 		if ((fwtrace_info->prev_seq + 1) != incoming_seq) {
182 			DHD_ERROR(("*** seq mismatch, index = %u, length = %u, "
183 				"cur seq = %u, in-seq = %u \n",
184 				trace_buf_index, length,
185 				fwtrace_info->prev_seq, incoming_seq));
186 		}
187 		fwtrace_info->prev_seq = incoming_seq;
188 
189 		trace_buf_index++;
190 		trace_buf_index &= (fwtrace_info->trace_buf_count - 1u);
191 		fwtrace_info->trace_buf_index = trace_buf_index;
192 	} while (true);
193 
194 done:
195 	mutex_unlock(&fwtrace_info->fwtrace_lock);
196 	return;
197 }
198 
199 /*
200  * Write the incoming trace data to a file. The maximum file size is 1MB. After that
201  * the trace data is saved into a new file.
202  */
203 static int
fwtrace_write_to_file(uint8 * buf,uint16 buf_len,dhd_pub_t * dhdp)204 fwtrace_write_to_file(uint8 *buf, uint16 buf_len, dhd_pub_t *dhdp)
205 {
206 	fwtrace_info_t *fwtrace_info = dhdp->fwtrace_info;
207 	int ret_val = BCME_OK;
208 	int ret_val_1 = 0;
209 	mm_segment_t old_fs;
210 	loff_t pos = 0;
211 	struct kstat stat;
212 	int error;
213 
214 	/* Change to KERNEL_DS address limit */
215 	old_fs = get_fs();
216 	set_fs(KERNEL_DS);
217 
218 	if (buf == NULL) {
219 		ret_val = BCME_ERROR;
220 		goto done;
221 	}
222 
223 	if (IS_ERR_OR_NULL(fwtrace_info->fw_trace_fp)) {
224 		ret_val = BCME_ERROR;
225 		goto done;
226 	}
227 
228 	//
229 	// Get the file size
230 	// if the size + buf_len > TRACE_FILE_SIZE, then write to a different file.
231 	//
232 	error = vfs_stat(fwtrace_info->trace_file, &stat);
233 	if (error) {
234 		DHD_ERROR(("vfs_stat has failed with error code = %d\n", error));
235 		goto done;
236 	}
237 
238 	if ((int) stat.size + buf_len > TRACE_FILE_SIZE) {
239 		fwtrace_close_file(dhdp);
240 		(fwtrace_info->part_index)++;
241 		fwtrace_open_file(TRUE, dhdp);
242 	}
243 
244 	pos = fwtrace_info->fw_trace_fp->f_pos;
245 	/* Write buf to file */
246 	ret_val_1 = vfs_write(fwtrace_info->fw_trace_fp,
247 	                      (char *) buf, (uint32) buf_len, &pos);
248 	if (ret_val_1 < 0) {
249 		DHD_ERROR(("write file error, err = %d\n", ret_val_1));
250 		ret_val = BCME_ERROR;
251 		goto done;
252 	}
253 	fwtrace_info->fw_trace_fp->f_pos = pos;
254 
255 	/* Sync file from filesystem to physical media */
256 	ret_val_1 = vfs_fsync(fwtrace_info->fw_trace_fp, 0);
257 	if (ret_val_1 < 0) {
258 		DHD_ERROR(("sync file error, error = %d\n", ret_val_1));
259 		ret_val = BCME_ERROR;
260 		goto done;
261 	}
262 
263 done:
264 	/* restore previous address limit */
265 	set_fs(old_fs);
266 	return (ret_val);
267 }
268 
269 /*
270  * Start the trace, gets called from the ioctl handler.
271  */
272 int
fw_trace_start(dhd_pub_t * dhdp,uint32 fw_trace_enabled)273 fw_trace_start(dhd_pub_t *dhdp, uint32 fw_trace_enabled)
274 {
275 	int ret_val = BCME_OK;
276 
277 	(dhdp->fwtrace_info->file_index)++;
278 	dhdp->fwtrace_info->part_index = 1;
279 
280 	dhdp->fwtrace_info->trace_buf_index = 0;
281 
282 	mutex_lock(&dhdp->fwtrace_info->fwtrace_lock);
283 	ret_val = fwtrace_open_file(fw_trace_enabled, dhdp);
284 	if (ret_val == BCME_OK) {
285 		dhdp->fwtrace_info->fwtrace_enable = fw_trace_enabled;
286 	}
287 	mutex_unlock(&dhdp->fwtrace_info->fwtrace_lock);
288 
289 	return (ret_val);
290 }
291 
292 /*
293  * Stop the trace collection and close the file descriptor.
294  */
295 int
fw_trace_stop(dhd_pub_t * dhdp)296 fw_trace_stop(dhd_pub_t *dhdp)
297 {
298 	int ret_val = BCME_OK;
299 
300 	/* Check to see if there is any trace data */
301 	process_fw_trace_data(dhdp);
302 
303 	mutex_lock(&dhdp->fwtrace_info->fwtrace_lock); /* acquire lock */
304 	/* flush the trace buffer */
305 	ret_val = fwtrace_close_file(dhdp);
306 
307 	/* free the trace buffer */
308 	fwtrace_free_trace_buf(dhdp);
309 	mutex_unlock(&dhdp->fwtrace_info->fwtrace_lock); /* release the lock */
310 
311 	return (ret_val);
312 }
313 
314 /*
315  * The trace file format is: fw_trace_w_part_x_y_z
316  *     where w is the file index, x is the part index,
317  *	     y is in seconds and z is in milliseconds
318  *
319  *     fw_trace_1_part_1_1539298163209110
320  *     fw_trace_1_part_2_1539298194739003  etc.
321  *
322  */
323 static int
fwtrace_open_file(uint32 fw_trace_enabled,dhd_pub_t * dhdp)324 fwtrace_open_file(uint32 fw_trace_enabled, dhd_pub_t *dhdp)
325 {
326 	fwtrace_info_t *fwtrace_info = dhdp->fwtrace_info;
327 	int ret_val = BCME_OK;
328 	uint32 file_mode;
329 	char ts_str[DEBUG_DUMP_TIME_BUF_LEN];
330 
331 	if (fw_trace_enabled) {
332 		if (!(IS_ERR_OR_NULL(fwtrace_info->fw_trace_fp))) {
333 			(void) filp_close(fwtrace_info->fw_trace_fp, 0);
334 		}
335 
336 		DHD_INFO((" *** Creating the trace file \n"));
337 
338 		file_mode = O_CREAT | O_WRONLY | O_SYNC;
339 		clear_debug_dump_time(ts_str);
340 		get_debug_dump_time(ts_str);
341 
342 		snprintf(fwtrace_info->trace_file,
343 		         sizeof(fwtrace_info->trace_file),
344 		         "%sfw_trace_%d_part_%d_%x_%s",
345 		         DHD_COMMON_DUMP_PATH, fwtrace_info->file_index,
346 		         fwtrace_info->part_index,
347 		         dhd_bus_get_bp_base(dhdp),
348 		         ts_str);
349 
350 		fwtrace_info->fw_trace_fp =
351 		        filp_open(fwtrace_info->trace_file, file_mode, 0664);
352 
353 		if (IS_ERR(fwtrace_info->fw_trace_fp)) {
354 			DHD_ERROR(("Unable to create the fw trace file file: %s\n",
355 			           fwtrace_info->trace_file));
356 			ret_val = BCME_ERROR;
357 			goto done;
358 		}
359 	}
360 
361 done:
362 	return (ret_val);
363 }
364 
365 static int
fwtrace_close_file(dhd_pub_t * dhdp)366 fwtrace_close_file(dhd_pub_t *dhdp)
367 {
368 	int ret_val = BCME_OK;
369 
370 	if (!(IS_ERR_OR_NULL(dhdp->fwtrace_info->fw_trace_fp))) {
371 		(void) filp_close(dhdp->fwtrace_info->fw_trace_fp, 0);
372 	}
373 
374 	dhdp->fwtrace_info->fw_trace_fp = NULL;
375 
376 	return (ret_val);
377 }
378 
379 #define FWTRACE_HADDR_PARAMS_SIZE	256u
380 #define FW_TRACE_FLUSH			0x8u /* bit 3 */
381 
382 static int send_fw_trace_val(dhd_pub_t *dhdp, int val);
383 
384 /*
385  * Initialize FWTRACE.
386  * Allocate trace buffer and open trace file.
387  */
388 int
fwtrace_init(dhd_pub_t * dhdp)389 fwtrace_init(dhd_pub_t *dhdp)
390 {
391 	int ret_val = BCME_OK;
392 	fwtrace_hostaddr_info_t host_buf_info;
393 
394 	if (dhdp->fwtrace_info->fwtrace_buf.va != NULL) {
395 		/* Already initialized */
396 		goto done;
397 	}
398 
399 	ret_val = fwtrace_get_haddr(dhdp, &host_buf_info);
400 
401 	if (ret_val != BCME_OK) {
402 		goto done;
403 	}
404 
405 	DHD_INFO(("dhd_get_trace_haddr: addr = %llx, len = %u\n",
406 		host_buf_info.haddr.u64, host_buf_info.num_bufs));
407 
408 	/* Initialize and setup the file */
409 	ret_val = fw_trace_start(dhdp, TRUE);
410 
411 done:
412 	return ret_val;
413 }
414 
415 /*
416  * Process the fwtrace set command to enable/disable firmware tracing.
417  * Always, enable preceeds with disable.
418  */
419 int
handle_set_fwtrace(dhd_pub_t * dhdp,uint32 val)420 handle_set_fwtrace(dhd_pub_t *dhdp, uint32 val)
421 {
422 	int ret, ret_val = BCME_OK;
423 
424 	/* On set, consider only lower two bytes for now */
425 	dhdp->fwtrace_info->fwtrace_enable = (val & 0xFFFF);
426 
427 	if (val & FW_TRACE_FLUSH) { /* only flush the trace buffer */
428 		if ((ret_val = send_fw_trace_val(dhdp, val)) != BCME_OK) {
429 			goto done;
430 		}
431 	} else if (val == 0) { /* disable the tracing */
432 		/* Disable the trace in the firmware */
433 		if ((ret_val = send_fw_trace_val(dhdp, val)) != BCME_OK) {
434 			goto done;
435 		}
436 
437 		/* cleanup in the driver */
438 		fw_trace_stop(dhdp);
439 	} else {		/* enable the tracing */
440 		fwtrace_hostaddr_info_t haddr_info;
441 
442 		ret_val = fwtrace_init(dhdp);
443 		if (ret_val != BCME_OK) {
444 			goto done;
445 		}
446 
447 		if ((ret_val = fwtrace_get_haddr(dhdp, &haddr_info)) != BCME_OK) {
448 			DHD_ERROR(("%s: set dhd_iovar has failed for "
449 				"fw_trace_haddr, "
450 				"ret=%d\n", __FUNCTION__, ret_val));
451 			goto done;
452 		}
453 
454 		ret = dhd_iovar(dhdp, 0, "dngl:fwtrace_haddr",
455 		                (char *) &haddr_info, sizeof(haddr_info),
456 		                NULL, 0, TRUE);
457 		if (ret < 0) {
458 			DHD_ERROR(("%s: set dhd_iovar has failed for "
459 			           "fwtrace_haddr, "
460 			           "ret=%d\n", __FUNCTION__, ret));
461 			ret_val = BCME_NOMEM;
462 			goto done;
463 		}
464 
465 		/* Finaly, enable the trace in the firmware */
466 		if ((ret_val = send_fw_trace_val(dhdp, val)) != BCME_OK) {
467 			goto done;
468 		}
469 	}
470 done:
471 	return (ret_val);
472 }
473 
474 /*
475  * Send dngl:fwtrace IOVAR to the firmware.
476  */
477 
478 static int
send_fw_trace_val(dhd_pub_t * dhdp,int val)479 send_fw_trace_val(dhd_pub_t *dhdp, int val)
480 {
481 	int ret_val = BCME_OK;
482 
483 	if ((ret_val = dhd_iovar(dhdp, 0, "dngl:fwtrace", (char *)&val, sizeof(val),
484 	                         NULL, 0, TRUE)) < 0) {
485 		DHD_ERROR(("%s: set dhd_iovar has failed fwtrace, "
486 		           "ret=%d\n", __FUNCTION__, ret_val));
487 	}
488 
489 	return (ret_val);
490 }
491 
492 /*
493  * Returns the virual address for the firmware trace buffer.
494  * DHD monitors this buffer for an update from the firmware.
495  */
496 static fwtrace_buf_t *
fwtrace_get_trace_data_ptr(dhd_pub_t * dhdp)497 fwtrace_get_trace_data_ptr(dhd_pub_t *dhdp)
498 {
499 	return ((fwtrace_buf_t *) dhdp->fwtrace_info->fwtrace_buf.va);
500 }
501 
502 int
fwtrace_get_haddr(dhd_pub_t * dhdp,fwtrace_hostaddr_info_t * haddr_info)503 fwtrace_get_haddr(dhd_pub_t *dhdp,  fwtrace_hostaddr_info_t *haddr_info)
504 {
505 	int ret_val = BCME_NOMEM;
506 	int num_host_buffers = FWTRACE_NUM_HOST_BUFFERS;
507 
508 	if (haddr_info == NULL) {
509 		ret_val = BCME_BADARG;
510 		goto done;
511 	}
512 
513 	if (dhdp->fwtrace_info->fwtrace_buf.va != NULL) {
514 		/* Use the existing buffer and send to the firmware */
515 		haddr_info->haddr.u64 = HTOL64(*(uint64 *)
516 			&dhdp->fwtrace_info->fwtrace_buf.pa);
517 		haddr_info->num_bufs = dhdp->fwtrace_info->trace_buf_count;
518 		haddr_info->buf_len = sizeof(fwtrace_buf_t);
519 		ret_val = BCME_OK;
520 		goto done;
521 	}
522 
523 	do {
524 		/* Initialize firmware trace buffer */
525 		if (dhd_dma_buf_alloc(dhdp, &dhdp->fwtrace_info->fwtrace_buf,
526 			sizeof(fwtrace_buf_t) * num_host_buffers) == BCME_OK) {
527 			dhdp->fwtrace_info->trace_buf_count = num_host_buffers;
528 			ret_val = BCME_OK;
529 			break;
530 		}
531 
532 		DHD_ERROR(("%s: Allocing %d buffers of size %lu bytes failed\n",
533 			__FUNCTION__, num_host_buffers,
534 			sizeof(fwtrace_buf_t) * num_host_buffers));
535 
536 		/* Retry with smaller numbers */
537 		num_host_buffers >>= 1;
538 	} while (num_host_buffers > 0);
539 
540 	haddr_info->haddr.u64 = HTOL64(*(uint64 *)&dhdp->fwtrace_info->fwtrace_buf.pa);
541 	haddr_info->num_bufs = num_host_buffers;
542 	haddr_info->buf_len = sizeof(fwtrace_buf_t);
543 
544 	DHD_INFO(("Firmware trace buffer, host address = %llx, count = %u \n",
545 	          haddr_info->haddr.u64,
546 	          haddr_info->num_bufs));
547 done:
548 	return (ret_val);
549 }
550 
551 /*
552  * Frees the host buffer.
553  */
554 static void
fwtrace_free_trace_buf(dhd_pub_t * dhdp)555 fwtrace_free_trace_buf(dhd_pub_t *dhdp)
556 {
557 	dhd_dma_buf_free(dhdp, &dhdp->fwtrace_info->fwtrace_buf);
558 	return;
559 }
560 
561 #endif	/* DHD_FWTRACE */
562 
563 #endif /* BCMINTERNAL */
564