1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 2 // 3 // This file is provided under a dual BSD/GPLv2 license. When using or 4 // redistributing this file, you may do so under either license. 5 // 6 // Copyright(c) 2018 Intel Corporation. All rights reserved. 7 // 8 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> 9 // 10 11 #include <linux/debugfs.h> 12 #include <linux/sched/signal.h> 13 #include "sof-priv.h" 14 #include "ops.h" 15 #include "sof-utils.h" 16 17 #define TRACE_FILTER_ELEMENTS_PER_ENTRY 4 18 #define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024 19 20 static int trace_filter_append_elem(struct snd_sof_dev *sdev, uint32_t key, uint32_t value, 21 struct sof_ipc_trace_filter_elem *elem_list, 22 int capacity, int *counter) 23 { 24 if (*counter >= capacity) 25 return -ENOMEM; 26 27 elem_list[*counter].key = key; 28 elem_list[*counter].value = value; 29 ++*counter; 30 31 return 0; 32 } 33 34 static int trace_filter_parse_entry(struct snd_sof_dev *sdev, const char *line, 35 struct sof_ipc_trace_filter_elem *elem, 36 int capacity, int *counter) 37 { 38 int len = strlen(line); 39 int cnt = *counter; 40 uint32_t uuid_id; 41 int log_level; 42 int pipe_id; 43 int comp_id; 44 int read; 45 int ret; 46 47 /* ignore empty content */ 48 ret = sscanf(line, " %n", &read); 49 if (!ret && read == len) 50 return len; 51 52 ret = sscanf(line, " %d %x %d %d %n", &log_level, &uuid_id, &pipe_id, &comp_id, &read); 53 if (ret != TRACE_FILTER_ELEMENTS_PER_ENTRY || read != len) { 54 dev_err(sdev->dev, "error: invalid trace filter entry '%s'\n", line); 55 return -EINVAL; 56 } 57 58 if (uuid_id > 0) { 59 ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_UUID, 60 uuid_id, elem, capacity, &cnt); 61 if (ret) 62 return ret; 63 } 64 if (pipe_id >= 0) { 65 ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_PIPE, 66 pipe_id, elem, capacity, &cnt); 67 if (ret) 68 return ret; 69 } 70 if (comp_id >= 0) { 71 ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_COMP, 72 comp_id, elem, capacity, &cnt); 73 if (ret) 74 return ret; 75 } 76 77 ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_SET_LEVEL | 78 SOF_IPC_TRACE_FILTER_ELEM_FIN, 79 log_level, elem, capacity, &cnt); 80 if (ret) 81 return ret; 82 83 /* update counter only when parsing whole entry passed */ 84 *counter = cnt; 85 86 return len; 87 } 88 89 static int trace_filter_parse(struct snd_sof_dev *sdev, char *string, 90 int *out_elem_cnt, 91 struct sof_ipc_trace_filter_elem **out) 92 { 93 static const char entry_delimiter[] = ";"; 94 char *entry = string; 95 int capacity = 0; 96 int entry_len; 97 int cnt = 0; 98 99 /* 100 * Each entry contains at least 1, up to TRACE_FILTER_ELEMENTS_PER_ENTRY 101 * IPC elements, depending on content. Calculate IPC elements capacity 102 * for the input string where each element is set. 103 */ 104 while (entry) { 105 capacity += TRACE_FILTER_ELEMENTS_PER_ENTRY; 106 entry = strchr(entry + 1, entry_delimiter[0]); 107 } 108 *out = kmalloc(capacity * sizeof(**out), GFP_KERNEL); 109 if (!*out) 110 return -ENOMEM; 111 112 /* split input string by ';', and parse each entry separately in trace_filter_parse_entry */ 113 while ((entry = strsep(&string, entry_delimiter))) { 114 entry_len = trace_filter_parse_entry(sdev, entry, *out, capacity, &cnt); 115 if (entry_len < 0) { 116 dev_err(sdev->dev, "error: %s failed for '%s', %d\n", __func__, entry, 117 entry_len); 118 return -EINVAL; 119 } 120 } 121 122 *out_elem_cnt = cnt; 123 124 return 0; 125 } 126 127 static int sof_ipc_trace_update_filter(struct snd_sof_dev *sdev, int num_elems, 128 struct sof_ipc_trace_filter_elem *elems) 129 { 130 struct sof_ipc_trace_filter *msg; 131 struct sof_ipc_reply reply; 132 size_t size; 133 int ret; 134 135 size = struct_size(msg, elems, num_elems); 136 if (size > SOF_IPC_MSG_MAX_SIZE) 137 return -EINVAL; 138 139 msg = kmalloc(size, GFP_KERNEL); 140 if (!msg) 141 return -ENOMEM; 142 143 msg->hdr.size = size; 144 msg->hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_FILTER_UPDATE; 145 msg->elem_cnt = num_elems; 146 memcpy(&msg->elems[0], elems, num_elems * sizeof(*elems)); 147 148 ret = pm_runtime_get_sync(sdev->dev); 149 if (ret < 0 && ret != -EACCES) { 150 pm_runtime_put_noidle(sdev->dev); 151 dev_err(sdev->dev, "error: enabling device failed: %d\n", ret); 152 goto error; 153 } 154 ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, 155 &reply, sizeof(reply)); 156 pm_runtime_mark_last_busy(sdev->dev); 157 pm_runtime_put_autosuspend(sdev->dev); 158 159 error: 160 kfree(msg); 161 return ret ? ret : reply.error; 162 } 163 164 static ssize_t sof_dfsentry_trace_filter_write(struct file *file, const char __user *from, 165 size_t count, loff_t *ppos) 166 { 167 struct snd_sof_dfsentry *dfse = file->private_data; 168 struct sof_ipc_trace_filter_elem *elems = NULL; 169 struct snd_sof_dev *sdev = dfse->sdev; 170 loff_t pos = 0; 171 int num_elems; 172 char *string; 173 int ret; 174 175 if (count > TRACE_FILTER_MAX_CONFIG_STRING_LENGTH) { 176 dev_err(sdev->dev, "%s too long input, %zu > %d\n", __func__, count, 177 TRACE_FILTER_MAX_CONFIG_STRING_LENGTH); 178 return -EINVAL; 179 } 180 181 string = kmalloc(count + 1, GFP_KERNEL); 182 if (!string) 183 return -ENOMEM; 184 185 /* assert null termination */ 186 string[count] = 0; 187 ret = simple_write_to_buffer(string, count, &pos, from, count); 188 if (ret < 0) 189 goto error; 190 191 ret = trace_filter_parse(sdev, string, &num_elems, &elems); 192 if (ret < 0) { 193 dev_err(sdev->dev, "error: fail in trace_filter_parse, %d\n", ret); 194 goto error; 195 } 196 197 if (num_elems) { 198 ret = sof_ipc_trace_update_filter(sdev, num_elems, elems); 199 if (ret < 0) { 200 dev_err(sdev->dev, "error: fail in sof_ipc_trace_update_filter %d\n", ret); 201 goto error; 202 } 203 } 204 ret = count; 205 error: 206 kfree(string); 207 kfree(elems); 208 return ret; 209 } 210 211 static const struct file_operations sof_dfs_trace_filter_fops = { 212 .open = simple_open, 213 .write = sof_dfsentry_trace_filter_write, 214 .llseek = default_llseek, 215 }; 216 217 static int trace_debugfs_filter_create(struct snd_sof_dev *sdev) 218 { 219 struct snd_sof_dfsentry *dfse; 220 221 dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); 222 if (!dfse) 223 return -ENOMEM; 224 225 dfse->sdev = sdev; 226 dfse->type = SOF_DFSENTRY_TYPE_BUF; 227 228 debugfs_create_file("filter", 0200, sdev->debugfs_root, dfse, 229 &sof_dfs_trace_filter_fops); 230 /* add to dfsentry list */ 231 list_add(&dfse->list, &sdev->dfsentry_list); 232 233 return 0; 234 } 235 236 static size_t sof_trace_avail(struct snd_sof_dev *sdev, 237 loff_t pos, size_t buffer_size) 238 { 239 loff_t host_offset = READ_ONCE(sdev->host_offset); 240 241 /* 242 * If host offset is less than local pos, it means write pointer of 243 * host DMA buffer has been wrapped. We should output the trace data 244 * at the end of host DMA buffer at first. 245 */ 246 if (host_offset < pos) 247 return buffer_size - pos; 248 249 /* If there is available trace data now, it is unnecessary to wait. */ 250 if (host_offset > pos) 251 return host_offset - pos; 252 253 return 0; 254 } 255 256 static size_t sof_wait_trace_avail(struct snd_sof_dev *sdev, 257 loff_t pos, size_t buffer_size) 258 { 259 wait_queue_entry_t wait; 260 size_t ret = sof_trace_avail(sdev, pos, buffer_size); 261 262 /* data immediately available */ 263 if (ret) 264 return ret; 265 266 if (!sdev->dtrace_is_enabled && sdev->dtrace_draining) { 267 /* 268 * tracing has ended and all traces have been 269 * read by client, return EOF 270 */ 271 sdev->dtrace_draining = false; 272 return 0; 273 } 274 275 /* wait for available trace data from FW */ 276 init_waitqueue_entry(&wait, current); 277 set_current_state(TASK_INTERRUPTIBLE); 278 add_wait_queue(&sdev->trace_sleep, &wait); 279 280 if (!signal_pending(current)) { 281 /* set timeout to max value, no error code */ 282 schedule_timeout(MAX_SCHEDULE_TIMEOUT); 283 } 284 remove_wait_queue(&sdev->trace_sleep, &wait); 285 286 return sof_trace_avail(sdev, pos, buffer_size); 287 } 288 289 static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, 290 size_t count, loff_t *ppos) 291 { 292 struct snd_sof_dfsentry *dfse = file->private_data; 293 struct snd_sof_dev *sdev = dfse->sdev; 294 unsigned long rem; 295 loff_t lpos = *ppos; 296 size_t avail, buffer_size = dfse->size; 297 u64 lpos_64; 298 299 /* make sure we know about any failures on the DSP side */ 300 sdev->dtrace_error = false; 301 302 /* check pos and count */ 303 if (lpos < 0) 304 return -EINVAL; 305 if (!count) 306 return 0; 307 308 /* check for buffer wrap and count overflow */ 309 lpos_64 = lpos; 310 lpos = do_div(lpos_64, buffer_size); 311 312 /* get available count based on current host offset */ 313 avail = sof_wait_trace_avail(sdev, lpos, buffer_size); 314 if (sdev->dtrace_error) { 315 dev_err(sdev->dev, "error: trace IO error\n"); 316 return -EIO; 317 } 318 319 /* make sure count is <= avail */ 320 if (count > avail) 321 count = avail; 322 323 /* 324 * make sure that all trace data is available for the CPU as the trace 325 * data buffer might be allocated from non consistent memory. 326 * Note: snd_dma_buffer_sync() is called for normal audio playback and 327 * capture streams also. 328 */ 329 snd_dma_buffer_sync(&sdev->dmatb, SNDRV_DMA_SYNC_CPU); 330 /* copy available trace data to debugfs */ 331 rem = copy_to_user(buffer, ((u8 *)(dfse->buf) + lpos), count); 332 if (rem) 333 return -EFAULT; 334 335 *ppos += count; 336 337 /* move debugfs reading position */ 338 return count; 339 } 340 341 static int sof_dfsentry_trace_release(struct inode *inode, struct file *file) 342 { 343 struct snd_sof_dfsentry *dfse = inode->i_private; 344 struct snd_sof_dev *sdev = dfse->sdev; 345 346 /* avoid duplicate traces at next open */ 347 if (!sdev->dtrace_is_enabled) 348 sdev->host_offset = 0; 349 350 return 0; 351 } 352 353 static const struct file_operations sof_dfs_trace_fops = { 354 .open = simple_open, 355 .read = sof_dfsentry_trace_read, 356 .llseek = default_llseek, 357 .release = sof_dfsentry_trace_release, 358 }; 359 360 static int trace_debugfs_create(struct snd_sof_dev *sdev) 361 { 362 struct snd_sof_dfsentry *dfse; 363 int ret; 364 365 if (!sdev) 366 return -EINVAL; 367 368 ret = trace_debugfs_filter_create(sdev); 369 if (ret < 0) 370 dev_err(sdev->dev, "error: fail in %s, %d", __func__, ret); 371 372 dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); 373 if (!dfse) 374 return -ENOMEM; 375 376 dfse->type = SOF_DFSENTRY_TYPE_BUF; 377 dfse->buf = sdev->dmatb.area; 378 dfse->size = sdev->dmatb.bytes; 379 dfse->sdev = sdev; 380 381 debugfs_create_file("trace", 0444, sdev->debugfs_root, dfse, 382 &sof_dfs_trace_fops); 383 384 return 0; 385 } 386 387 int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev) 388 { 389 struct sof_ipc_fw_ready *ready = &sdev->fw_ready; 390 struct sof_ipc_fw_version *v = &ready->version; 391 struct sof_ipc_dma_trace_params_ext params; 392 struct sof_ipc_reply ipc_reply; 393 int ret; 394 395 if (!sdev->dtrace_is_supported) 396 return 0; 397 398 if (sdev->dtrace_is_enabled || !sdev->dma_trace_pages) 399 return -EINVAL; 400 401 /* set IPC parameters */ 402 params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG; 403 /* PARAMS_EXT is only supported from ABI 3.7.0 onwards */ 404 if (v->abi_version >= SOF_ABI_VER(3, 7, 0)) { 405 params.hdr.size = sizeof(struct sof_ipc_dma_trace_params_ext); 406 params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS_EXT; 407 params.timestamp_ns = ktime_get(); /* in nanosecond */ 408 } else { 409 params.hdr.size = sizeof(struct sof_ipc_dma_trace_params); 410 params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS; 411 } 412 params.buffer.phy_addr = sdev->dmatp.addr; 413 params.buffer.size = sdev->dmatb.bytes; 414 params.buffer.pages = sdev->dma_trace_pages; 415 params.stream_tag = 0; 416 417 sdev->host_offset = 0; 418 sdev->dtrace_draining = false; 419 420 ret = snd_sof_dma_trace_init(sdev, ¶ms); 421 if (ret < 0) { 422 dev_err(sdev->dev, 423 "error: fail in snd_sof_dma_trace_init %d\n", ret); 424 return ret; 425 } 426 dev_dbg(sdev->dev, "%s: stream_tag: %d\n", __func__, params.stream_tag); 427 428 /* send IPC to the DSP */ 429 ret = sof_ipc_tx_message(sdev->ipc, 430 params.hdr.cmd, ¶ms, sizeof(params), 431 &ipc_reply, sizeof(ipc_reply)); 432 if (ret < 0) { 433 dev_err(sdev->dev, 434 "error: can't set params for DMA for trace %d\n", ret); 435 goto trace_release; 436 } 437 438 ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_START); 439 if (ret < 0) { 440 dev_err(sdev->dev, 441 "error: snd_sof_dma_trace_trigger: start: %d\n", ret); 442 goto trace_release; 443 } 444 445 sdev->dtrace_is_enabled = true; 446 447 return 0; 448 449 trace_release: 450 snd_sof_dma_trace_release(sdev); 451 return ret; 452 } 453 454 int snd_sof_init_trace(struct snd_sof_dev *sdev) 455 { 456 int ret; 457 458 if (!sdev->dtrace_is_supported) 459 return 0; 460 461 /* set false before start initialization */ 462 sdev->dtrace_is_enabled = false; 463 464 /* allocate trace page table buffer */ 465 ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev, 466 PAGE_SIZE, &sdev->dmatp); 467 if (ret < 0) { 468 dev_err(sdev->dev, 469 "error: can't alloc page table for trace %d\n", ret); 470 return ret; 471 } 472 473 /* allocate trace data buffer */ 474 ret = snd_dma_alloc_dir_pages(SNDRV_DMA_TYPE_DEV_SG, sdev->dev, 475 DMA_FROM_DEVICE, DMA_BUF_SIZE_FOR_TRACE, 476 &sdev->dmatb); 477 if (ret < 0) { 478 dev_err(sdev->dev, 479 "error: can't alloc buffer for trace %d\n", ret); 480 goto page_err; 481 } 482 483 /* create compressed page table for audio firmware */ 484 ret = snd_sof_create_page_table(sdev->dev, &sdev->dmatb, 485 sdev->dmatp.area, sdev->dmatb.bytes); 486 if (ret < 0) 487 goto table_err; 488 489 sdev->dma_trace_pages = ret; 490 dev_dbg(sdev->dev, "%s: dma_trace_pages: %d\n", 491 __func__, sdev->dma_trace_pages); 492 493 if (sdev->first_boot) { 494 ret = trace_debugfs_create(sdev); 495 if (ret < 0) 496 goto table_err; 497 } 498 499 init_waitqueue_head(&sdev->trace_sleep); 500 501 ret = snd_sof_init_trace_ipc(sdev); 502 if (ret < 0) 503 goto table_err; 504 505 return 0; 506 table_err: 507 sdev->dma_trace_pages = 0; 508 snd_dma_free_pages(&sdev->dmatb); 509 page_err: 510 snd_dma_free_pages(&sdev->dmatp); 511 return ret; 512 } 513 EXPORT_SYMBOL(snd_sof_init_trace); 514 515 int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, 516 struct sof_ipc_dma_trace_posn *posn) 517 { 518 if (!sdev->dtrace_is_supported) 519 return 0; 520 521 if (sdev->dtrace_is_enabled && sdev->host_offset != posn->host_offset) { 522 sdev->host_offset = posn->host_offset; 523 wake_up(&sdev->trace_sleep); 524 } 525 526 if (posn->overflow != 0) 527 dev_err(sdev->dev, 528 "error: DSP trace buffer overflow %u bytes. Total messages %d\n", 529 posn->overflow, posn->messages); 530 531 return 0; 532 } 533 534 /* an error has occurred within the DSP that prevents further trace */ 535 void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev) 536 { 537 if (!sdev->dtrace_is_supported) 538 return; 539 540 if (sdev->dtrace_is_enabled) { 541 sdev->dtrace_error = true; 542 wake_up(&sdev->trace_sleep); 543 } 544 } 545 EXPORT_SYMBOL(snd_sof_trace_notify_for_error); 546 547 void snd_sof_release_trace(struct snd_sof_dev *sdev) 548 { 549 struct sof_ipc_fw_ready *ready = &sdev->fw_ready; 550 struct sof_ipc_fw_version *v = &ready->version; 551 struct sof_ipc_cmd_hdr hdr; 552 struct sof_ipc_reply ipc_reply; 553 int ret; 554 555 if (!sdev->dtrace_is_supported || !sdev->dtrace_is_enabled) 556 return; 557 558 ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_STOP); 559 if (ret < 0) 560 dev_err(sdev->dev, 561 "error: snd_sof_dma_trace_trigger: stop: %d\n", ret); 562 563 /* 564 * stop and free trace DMA in the DSP. TRACE_DMA_FREE is only supported from 565 * ABI 3.20.0 onwards 566 */ 567 if (v->abi_version >= SOF_ABI_VER(3, 20, 0)) { 568 hdr.size = sizeof(hdr); 569 hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_FREE; 570 571 ret = sof_ipc_tx_message(sdev->ipc, hdr.cmd, &hdr, hdr.size, 572 &ipc_reply, sizeof(ipc_reply)); 573 if (ret < 0) 574 dev_err(sdev->dev, "DMA_TRACE_FREE failed with error: %d\n", ret); 575 } 576 577 ret = snd_sof_dma_trace_release(sdev); 578 if (ret < 0) 579 dev_err(sdev->dev, 580 "error: fail in snd_sof_dma_trace_release %d\n", ret); 581 582 sdev->dtrace_is_enabled = false; 583 sdev->dtrace_draining = true; 584 wake_up(&sdev->trace_sleep); 585 } 586 EXPORT_SYMBOL(snd_sof_release_trace); 587 588 void snd_sof_free_trace(struct snd_sof_dev *sdev) 589 { 590 if (!sdev->dtrace_is_supported) 591 return; 592 593 snd_sof_release_trace(sdev); 594 595 if (sdev->dma_trace_pages) { 596 snd_dma_free_pages(&sdev->dmatb); 597 snd_dma_free_pages(&sdev->dmatp); 598 sdev->dma_trace_pages = 0; 599 } 600 } 601 EXPORT_SYMBOL(snd_sof_free_trace); 602