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 // Generic debug routines used to export DSP MMIO and memories to userspace 11 // for firmware debugging. 12 // 13 14 #include <linux/debugfs.h> 15 #include <linux/io.h> 16 #include <linux/pm_runtime.h> 17 #include <sound/sof/ext_manifest.h> 18 #include <sound/sof/debug.h> 19 #include "sof-priv.h" 20 #include "ops.h" 21 22 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) 23 #include "sof-probes.h" 24 25 /** 26 * strsplit_u32 - Split string into sequence of u32 tokens 27 * @buf: String to split into tokens. 28 * @delim: String containing delimiter characters. 29 * @tkns: Returned u32 sequence pointer. 30 * @num_tkns: Returned number of tokens obtained. 31 */ 32 static int 33 strsplit_u32(char **buf, const char *delim, u32 **tkns, size_t *num_tkns) 34 { 35 char *s; 36 u32 *data, *tmp; 37 size_t count = 0; 38 size_t cap = 32; 39 int ret = 0; 40 41 *tkns = NULL; 42 *num_tkns = 0; 43 data = kcalloc(cap, sizeof(*data), GFP_KERNEL); 44 if (!data) 45 return -ENOMEM; 46 47 while ((s = strsep(buf, delim)) != NULL) { 48 ret = kstrtouint(s, 0, data + count); 49 if (ret) 50 goto exit; 51 if (++count >= cap) { 52 cap *= 2; 53 tmp = krealloc(data, cap * sizeof(*data), GFP_KERNEL); 54 if (!tmp) { 55 ret = -ENOMEM; 56 goto exit; 57 } 58 data = tmp; 59 } 60 } 61 62 if (!count) 63 goto exit; 64 *tkns = kmemdup(data, count * sizeof(*data), GFP_KERNEL); 65 if (*tkns == NULL) { 66 ret = -ENOMEM; 67 goto exit; 68 } 69 *num_tkns = count; 70 71 exit: 72 kfree(data); 73 return ret; 74 } 75 76 static int tokenize_input(const char __user *from, size_t count, 77 loff_t *ppos, u32 **tkns, size_t *num_tkns) 78 { 79 char *buf; 80 int ret; 81 82 buf = kmalloc(count + 1, GFP_KERNEL); 83 if (!buf) 84 return -ENOMEM; 85 86 ret = simple_write_to_buffer(buf, count, ppos, from, count); 87 if (ret != count) { 88 ret = ret >= 0 ? -EIO : ret; 89 goto exit; 90 } 91 92 buf[count] = '\0'; 93 ret = strsplit_u32((char **)&buf, ",", tkns, num_tkns); 94 exit: 95 kfree(buf); 96 return ret; 97 } 98 99 static ssize_t probe_points_read(struct file *file, 100 char __user *to, size_t count, loff_t *ppos) 101 { 102 struct snd_sof_dfsentry *dfse = file->private_data; 103 struct snd_sof_dev *sdev = dfse->sdev; 104 struct sof_probe_point_desc *desc; 105 size_t num_desc, len = 0; 106 char *buf; 107 int i, ret; 108 109 if (sdev->extractor_stream_tag == SOF_PROBE_INVALID_NODE_ID) { 110 dev_warn(sdev->dev, "no extractor stream running\n"); 111 return -ENOENT; 112 } 113 114 buf = kzalloc(PAGE_SIZE, GFP_KERNEL); 115 if (!buf) 116 return -ENOMEM; 117 118 ret = sof_ipc_probe_points_info(sdev, &desc, &num_desc); 119 if (ret < 0) 120 goto exit; 121 122 for (i = 0; i < num_desc; i++) { 123 ret = snprintf(buf + len, PAGE_SIZE - len, 124 "Id: %#010x Purpose: %d Node id: %#x\n", 125 desc[i].buffer_id, desc[i].purpose, desc[i].stream_tag); 126 if (ret < 0) 127 goto free_desc; 128 len += ret; 129 } 130 131 ret = simple_read_from_buffer(to, count, ppos, buf, len); 132 free_desc: 133 kfree(desc); 134 exit: 135 kfree(buf); 136 return ret; 137 } 138 139 static ssize_t probe_points_write(struct file *file, 140 const char __user *from, size_t count, loff_t *ppos) 141 { 142 struct snd_sof_dfsentry *dfse = file->private_data; 143 struct snd_sof_dev *sdev = dfse->sdev; 144 struct sof_probe_point_desc *desc; 145 size_t num_tkns, bytes; 146 u32 *tkns; 147 int ret; 148 149 if (sdev->extractor_stream_tag == SOF_PROBE_INVALID_NODE_ID) { 150 dev_warn(sdev->dev, "no extractor stream running\n"); 151 return -ENOENT; 152 } 153 154 ret = tokenize_input(from, count, ppos, &tkns, &num_tkns); 155 if (ret < 0) 156 return ret; 157 bytes = sizeof(*tkns) * num_tkns; 158 if (!num_tkns || (bytes % sizeof(*desc))) { 159 ret = -EINVAL; 160 goto exit; 161 } 162 163 desc = (struct sof_probe_point_desc *)tkns; 164 ret = sof_ipc_probe_points_add(sdev, 165 desc, bytes / sizeof(*desc)); 166 if (!ret) 167 ret = count; 168 exit: 169 kfree(tkns); 170 return ret; 171 } 172 173 static const struct file_operations probe_points_fops = { 174 .open = simple_open, 175 .read = probe_points_read, 176 .write = probe_points_write, 177 .llseek = default_llseek, 178 }; 179 180 static ssize_t probe_points_remove_write(struct file *file, 181 const char __user *from, size_t count, loff_t *ppos) 182 { 183 struct snd_sof_dfsentry *dfse = file->private_data; 184 struct snd_sof_dev *sdev = dfse->sdev; 185 size_t num_tkns; 186 u32 *tkns; 187 int ret; 188 189 if (sdev->extractor_stream_tag == SOF_PROBE_INVALID_NODE_ID) { 190 dev_warn(sdev->dev, "no extractor stream running\n"); 191 return -ENOENT; 192 } 193 194 ret = tokenize_input(from, count, ppos, &tkns, &num_tkns); 195 if (ret < 0) 196 return ret; 197 if (!num_tkns) { 198 ret = -EINVAL; 199 goto exit; 200 } 201 202 ret = sof_ipc_probe_points_remove(sdev, tkns, num_tkns); 203 if (!ret) 204 ret = count; 205 exit: 206 kfree(tkns); 207 return ret; 208 } 209 210 static const struct file_operations probe_points_remove_fops = { 211 .open = simple_open, 212 .write = probe_points_remove_write, 213 .llseek = default_llseek, 214 }; 215 216 static int snd_sof_debugfs_probe_item(struct snd_sof_dev *sdev, 217 const char *name, mode_t mode, 218 const struct file_operations *fops) 219 { 220 struct snd_sof_dfsentry *dfse; 221 222 dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); 223 if (!dfse) 224 return -ENOMEM; 225 226 dfse->type = SOF_DFSENTRY_TYPE_BUF; 227 dfse->sdev = sdev; 228 229 debugfs_create_file(name, mode, sdev->debugfs_root, dfse, fops); 230 /* add to dfsentry list */ 231 list_add(&dfse->list, &sdev->dfsentry_list); 232 233 return 0; 234 } 235 #endif 236 237 238 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) 239 static ssize_t msg_inject_read(struct file *file, char __user *buffer, 240 size_t count, loff_t *ppos) 241 { 242 struct snd_sof_dfsentry *dfse = file->private_data; 243 struct sof_ipc_reply *rhdr = dfse->msg_inject_rx; 244 245 if (!rhdr->hdr.size || !count || *ppos) 246 return 0; 247 248 if (count > rhdr->hdr.size) 249 count = rhdr->hdr.size; 250 251 if (copy_to_user(buffer, dfse->msg_inject_rx, count)) 252 return -EFAULT; 253 254 *ppos += count; 255 return count; 256 } 257 258 static ssize_t msg_inject_write(struct file *file, const char __user *buffer, 259 size_t count, loff_t *ppos) 260 { 261 struct snd_sof_dfsentry *dfse = file->private_data; 262 struct snd_sof_dev *sdev = dfse->sdev; 263 struct sof_ipc_cmd_hdr *hdr = dfse->msg_inject_tx; 264 size_t size; 265 int ret, err; 266 267 if (*ppos) 268 return 0; 269 270 size = simple_write_to_buffer(dfse->msg_inject_tx, SOF_IPC_MSG_MAX_SIZE, 271 ppos, buffer, count); 272 if (size != count) 273 return size > 0 ? -EFAULT : size; 274 275 ret = pm_runtime_get_sync(sdev->dev); 276 if (ret < 0 && ret != -EACCES) { 277 dev_err_ratelimited(sdev->dev, "%s: DSP resume failed: %d\n", 278 __func__, ret); 279 pm_runtime_put_noidle(sdev->dev); 280 goto out; 281 } 282 283 /* send the message */ 284 memset(dfse->msg_inject_rx, 0, SOF_IPC_MSG_MAX_SIZE); 285 ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, dfse->msg_inject_tx, count, 286 dfse->msg_inject_rx, SOF_IPC_MSG_MAX_SIZE); 287 288 pm_runtime_mark_last_busy(sdev->dev); 289 err = pm_runtime_put_autosuspend(sdev->dev); 290 if (err < 0) 291 dev_err_ratelimited(sdev->dev, "%s: DSP idle failed: %d\n", 292 __func__, err); 293 294 /* return size if test is successful */ 295 if (ret >= 0) 296 ret = size; 297 298 out: 299 return ret; 300 } 301 302 static const struct file_operations msg_inject_fops = { 303 .open = simple_open, 304 .read = msg_inject_read, 305 .write = msg_inject_write, 306 .llseek = default_llseek, 307 }; 308 309 static int snd_sof_debugfs_msg_inject_item(struct snd_sof_dev *sdev, 310 const char *name, mode_t mode, 311 const struct file_operations *fops) 312 { 313 struct snd_sof_dfsentry *dfse; 314 315 dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); 316 if (!dfse) 317 return -ENOMEM; 318 319 /* pre allocate the tx and rx buffers */ 320 dfse->msg_inject_tx = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); 321 dfse->msg_inject_rx = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); 322 if (!dfse->msg_inject_tx || !dfse->msg_inject_rx) 323 return -ENOMEM; 324 325 dfse->type = SOF_DFSENTRY_TYPE_BUF; 326 dfse->sdev = sdev; 327 328 debugfs_create_file(name, mode, sdev->debugfs_root, dfse, fops); 329 /* add to dfsentry list */ 330 list_add(&dfse->list, &sdev->dfsentry_list); 331 332 return 0; 333 } 334 #endif 335 336 static ssize_t sof_dfsentry_write(struct file *file, const char __user *buffer, 337 size_t count, loff_t *ppos) 338 { 339 size_t size; 340 char *string; 341 int ret; 342 343 string = kzalloc(count+1, GFP_KERNEL); 344 if (!string) 345 return -ENOMEM; 346 347 size = simple_write_to_buffer(string, count, ppos, buffer, count); 348 ret = size; 349 350 kfree(string); 351 return ret; 352 } 353 354 static ssize_t sof_dfsentry_read(struct file *file, char __user *buffer, 355 size_t count, loff_t *ppos) 356 { 357 struct snd_sof_dfsentry *dfse = file->private_data; 358 struct snd_sof_dev *sdev = dfse->sdev; 359 loff_t pos = *ppos; 360 size_t size_ret; 361 int skip = 0; 362 int size; 363 u8 *buf; 364 365 size = dfse->size; 366 367 /* validate position & count */ 368 if (pos < 0) 369 return -EINVAL; 370 if (pos >= size || !count) 371 return 0; 372 /* find the minimum. min() is not used since it adds sparse warnings */ 373 if (count > size - pos) 374 count = size - pos; 375 376 /* align io read start to u32 multiple */ 377 pos = ALIGN_DOWN(pos, 4); 378 379 /* intermediate buffer size must be u32 multiple */ 380 size = ALIGN(count, 4); 381 382 /* if start position is unaligned, read extra u32 */ 383 if (unlikely(pos != *ppos)) { 384 skip = *ppos - pos; 385 if (pos + size + 4 < dfse->size) 386 size += 4; 387 } 388 389 buf = kzalloc(size, GFP_KERNEL); 390 if (!buf) 391 return -ENOMEM; 392 393 if (dfse->type == SOF_DFSENTRY_TYPE_IOMEM) { 394 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) 395 /* 396 * If the DSP is active: copy from IO. 397 * If the DSP is suspended: 398 * - Copy from IO if the memory is always accessible. 399 * - Otherwise, copy from cached buffer. 400 */ 401 if (pm_runtime_active(sdev->dev) || 402 dfse->access_type == SOF_DEBUGFS_ACCESS_ALWAYS) { 403 memcpy_fromio(buf, dfse->io_mem + pos, size); 404 } else { 405 dev_info(sdev->dev, 406 "Copying cached debugfs data\n"); 407 memcpy(buf, dfse->cache_buf + pos, size); 408 } 409 #else 410 /* if the DSP is in D3 */ 411 if (!pm_runtime_active(sdev->dev) && 412 dfse->access_type == SOF_DEBUGFS_ACCESS_D0_ONLY) { 413 dev_err(sdev->dev, 414 "error: debugfs entry cannot be read in DSP D3\n"); 415 kfree(buf); 416 return -EINVAL; 417 } 418 419 memcpy_fromio(buf, dfse->io_mem + pos, size); 420 #endif 421 } else { 422 memcpy(buf, ((u8 *)(dfse->buf) + pos), size); 423 } 424 425 /* copy to userspace */ 426 size_ret = copy_to_user(buffer, buf + skip, count); 427 428 kfree(buf); 429 430 /* update count & position if copy succeeded */ 431 if (size_ret) 432 return -EFAULT; 433 434 *ppos = pos + count; 435 436 return count; 437 } 438 439 static const struct file_operations sof_dfs_fops = { 440 .open = simple_open, 441 .read = sof_dfsentry_read, 442 .llseek = default_llseek, 443 .write = sof_dfsentry_write, 444 }; 445 446 /* create FS entry for debug files that can expose DSP memories, registers */ 447 static int snd_sof_debugfs_io_item(struct snd_sof_dev *sdev, 448 void __iomem *base, size_t size, 449 const char *name, 450 enum sof_debugfs_access_type access_type) 451 { 452 struct snd_sof_dfsentry *dfse; 453 454 if (!sdev) 455 return -EINVAL; 456 457 dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); 458 if (!dfse) 459 return -ENOMEM; 460 461 dfse->type = SOF_DFSENTRY_TYPE_IOMEM; 462 dfse->io_mem = base; 463 dfse->size = size; 464 dfse->sdev = sdev; 465 dfse->access_type = access_type; 466 467 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) 468 /* 469 * allocate cache buffer that will be used to save the mem window 470 * contents prior to suspend 471 */ 472 if (access_type == SOF_DEBUGFS_ACCESS_D0_ONLY) { 473 dfse->cache_buf = devm_kzalloc(sdev->dev, size, GFP_KERNEL); 474 if (!dfse->cache_buf) 475 return -ENOMEM; 476 } 477 #endif 478 479 debugfs_create_file(name, 0444, sdev->debugfs_root, dfse, 480 &sof_dfs_fops); 481 482 /* add to dfsentry list */ 483 list_add(&dfse->list, &sdev->dfsentry_list); 484 485 return 0; 486 } 487 488 int snd_sof_debugfs_add_region_item_iomem(struct snd_sof_dev *sdev, 489 enum snd_sof_fw_blk_type blk_type, u32 offset, 490 size_t size, const char *name, 491 enum sof_debugfs_access_type access_type) 492 { 493 int bar = snd_sof_dsp_get_bar_index(sdev, blk_type); 494 495 if (bar < 0) 496 return bar; 497 498 return snd_sof_debugfs_io_item(sdev, sdev->bar[bar] + offset, size, name, 499 access_type); 500 } 501 EXPORT_SYMBOL_GPL(snd_sof_debugfs_add_region_item_iomem); 502 503 /* create FS entry for debug files to expose kernel memory */ 504 int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev, 505 void *base, size_t size, 506 const char *name, mode_t mode) 507 { 508 struct snd_sof_dfsentry *dfse; 509 510 if (!sdev) 511 return -EINVAL; 512 513 dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); 514 if (!dfse) 515 return -ENOMEM; 516 517 dfse->type = SOF_DFSENTRY_TYPE_BUF; 518 dfse->buf = base; 519 dfse->size = size; 520 dfse->sdev = sdev; 521 522 debugfs_create_file(name, mode, sdev->debugfs_root, dfse, 523 &sof_dfs_fops); 524 /* add to dfsentry list */ 525 list_add(&dfse->list, &sdev->dfsentry_list); 526 527 return 0; 528 } 529 EXPORT_SYMBOL_GPL(snd_sof_debugfs_buf_item); 530 531 static int memory_info_update(struct snd_sof_dev *sdev, char *buf, size_t buff_size) 532 { 533 struct sof_ipc_cmd_hdr msg = { 534 .size = sizeof(struct sof_ipc_cmd_hdr), 535 .cmd = SOF_IPC_GLB_DEBUG | SOF_IPC_DEBUG_MEM_USAGE, 536 }; 537 struct sof_ipc_dbg_mem_usage *reply; 538 int len; 539 int ret; 540 int i; 541 542 reply = kmalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); 543 if (!reply) 544 return -ENOMEM; 545 546 ret = pm_runtime_get_sync(sdev->dev); 547 if (ret < 0 && ret != -EACCES) { 548 pm_runtime_put_noidle(sdev->dev); 549 dev_err(sdev->dev, "error: enabling device failed: %d\n", ret); 550 goto error; 551 } 552 553 ret = sof_ipc_tx_message(sdev->ipc, msg.cmd, &msg, msg.size, reply, SOF_IPC_MSG_MAX_SIZE); 554 pm_runtime_mark_last_busy(sdev->dev); 555 pm_runtime_put_autosuspend(sdev->dev); 556 if (ret < 0 || reply->rhdr.error < 0) { 557 ret = min(ret, reply->rhdr.error); 558 dev_err(sdev->dev, "error: reading memory info failed, %d\n", ret); 559 goto error; 560 } 561 562 if (struct_size(reply, elems, reply->num_elems) != reply->rhdr.hdr.size) { 563 dev_err(sdev->dev, "error: invalid memory info ipc struct size, %d\n", 564 reply->rhdr.hdr.size); 565 ret = -EINVAL; 566 goto error; 567 } 568 569 for (i = 0, len = 0; i < reply->num_elems; i++) { 570 ret = snprintf(buf + len, buff_size - len, "zone %d.%d used %#8x free %#8x\n", 571 reply->elems[i].zone, reply->elems[i].id, 572 reply->elems[i].used, reply->elems[i].free); 573 if (ret < 0) 574 goto error; 575 len += ret; 576 } 577 578 ret = len; 579 error: 580 kfree(reply); 581 return ret; 582 } 583 584 static ssize_t memory_info_read(struct file *file, char __user *to, size_t count, loff_t *ppos) 585 { 586 struct snd_sof_dfsentry *dfse = file->private_data; 587 struct snd_sof_dev *sdev = dfse->sdev; 588 int data_length; 589 590 /* read memory info from FW only once for each file read */ 591 if (!*ppos) { 592 dfse->buf_data_size = 0; 593 data_length = memory_info_update(sdev, dfse->buf, dfse->size); 594 if (data_length < 0) 595 return data_length; 596 dfse->buf_data_size = data_length; 597 } 598 599 return simple_read_from_buffer(to, count, ppos, dfse->buf, dfse->buf_data_size); 600 } 601 602 static int memory_info_open(struct inode *inode, struct file *file) 603 { 604 struct snd_sof_dfsentry *dfse = inode->i_private; 605 struct snd_sof_dev *sdev = dfse->sdev; 606 607 file->private_data = dfse; 608 609 /* allocate buffer memory only in first open run, to save memory when unused */ 610 if (!dfse->buf) { 611 dfse->buf = devm_kmalloc(sdev->dev, PAGE_SIZE, GFP_KERNEL); 612 if (!dfse->buf) 613 return -ENOMEM; 614 dfse->size = PAGE_SIZE; 615 } 616 617 return 0; 618 } 619 620 static const struct file_operations memory_info_fops = { 621 .open = memory_info_open, 622 .read = memory_info_read, 623 .llseek = default_llseek, 624 }; 625 626 int snd_sof_dbg_memory_info_init(struct snd_sof_dev *sdev) 627 { 628 struct snd_sof_dfsentry *dfse; 629 630 dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); 631 if (!dfse) 632 return -ENOMEM; 633 634 /* don't allocate buffer before first usage, to save memory when unused */ 635 dfse->type = SOF_DFSENTRY_TYPE_BUF; 636 dfse->sdev = sdev; 637 638 debugfs_create_file("memory_info", 0444, sdev->debugfs_root, dfse, &memory_info_fops); 639 640 /* add to dfsentry list */ 641 list_add(&dfse->list, &sdev->dfsentry_list); 642 return 0; 643 } 644 EXPORT_SYMBOL_GPL(snd_sof_dbg_memory_info_init); 645 646 int snd_sof_dbg_init(struct snd_sof_dev *sdev) 647 { 648 const struct snd_sof_dsp_ops *ops = sof_ops(sdev); 649 const struct snd_sof_debugfs_map *map; 650 int i; 651 int err; 652 653 /* use "sof" as top level debugFS dir */ 654 sdev->debugfs_root = debugfs_create_dir("sof", NULL); 655 656 /* init dfsentry list */ 657 INIT_LIST_HEAD(&sdev->dfsentry_list); 658 659 /* create debugFS files for platform specific MMIO/DSP memories */ 660 for (i = 0; i < ops->debug_map_count; i++) { 661 map = &ops->debug_map[i]; 662 663 err = snd_sof_debugfs_io_item(sdev, sdev->bar[map->bar] + 664 map->offset, map->size, 665 map->name, map->access_type); 666 /* errors are only due to memory allocation, not debugfs */ 667 if (err < 0) 668 return err; 669 } 670 671 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) 672 err = snd_sof_debugfs_probe_item(sdev, "probe_points", 673 0644, &probe_points_fops); 674 if (err < 0) 675 return err; 676 err = snd_sof_debugfs_probe_item(sdev, "probe_points_remove", 677 0200, &probe_points_remove_fops); 678 if (err < 0) 679 return err; 680 #endif 681 682 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) 683 err = snd_sof_debugfs_msg_inject_item(sdev, "ipc_msg_inject", 0644, 684 &msg_inject_fops); 685 686 /* errors are only due to memory allocation, not debugfs */ 687 if (err < 0) 688 return err; 689 #endif 690 691 return 0; 692 } 693 EXPORT_SYMBOL_GPL(snd_sof_dbg_init); 694 695 void snd_sof_free_debug(struct snd_sof_dev *sdev) 696 { 697 debugfs_remove_recursive(sdev->debugfs_root); 698 } 699 EXPORT_SYMBOL_GPL(snd_sof_free_debug); 700 701 static const struct soc_fw_state_info { 702 enum sof_fw_state state; 703 const char *name; 704 } fw_state_dbg[] = { 705 {SOF_FW_BOOT_NOT_STARTED, "SOF_FW_BOOT_NOT_STARTED"}, 706 {SOF_FW_BOOT_PREPARE, "SOF_FW_BOOT_PREPARE"}, 707 {SOF_FW_BOOT_IN_PROGRESS, "SOF_FW_BOOT_IN_PROGRESS"}, 708 {SOF_FW_BOOT_FAILED, "SOF_FW_BOOT_FAILED"}, 709 {SOF_FW_BOOT_READY_FAILED, "SOF_FW_BOOT_READY_FAILED"}, 710 {SOF_FW_BOOT_READY_OK, "SOF_FW_BOOT_READY_OK"}, 711 {SOF_FW_BOOT_COMPLETE, "SOF_FW_BOOT_COMPLETE"}, 712 {SOF_FW_CRASHED, "SOF_FW_CRASHED"}, 713 }; 714 715 static void snd_sof_dbg_print_fw_state(struct snd_sof_dev *sdev, const char *level) 716 { 717 int i; 718 719 for (i = 0; i < ARRAY_SIZE(fw_state_dbg); i++) { 720 if (sdev->fw_state == fw_state_dbg[i].state) { 721 dev_printk(level, sdev->dev, "fw_state: %s (%d)\n", 722 fw_state_dbg[i].name, i); 723 return; 724 } 725 } 726 727 dev_printk(level, sdev->dev, "fw_state: UNKNOWN (%d)\n", sdev->fw_state); 728 } 729 730 void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, const char *msg, u32 flags) 731 { 732 char *level = flags & SOF_DBG_DUMP_OPTIONAL ? KERN_DEBUG : KERN_ERR; 733 bool print_all = sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS); 734 735 if (flags & SOF_DBG_DUMP_OPTIONAL && !print_all) 736 return; 737 738 if (sof_ops(sdev)->dbg_dump && !sdev->dbg_dump_printed) { 739 dev_printk(level, sdev->dev, 740 "------------[ DSP dump start ]------------\n"); 741 if (msg) 742 dev_printk(level, sdev->dev, "%s\n", msg); 743 snd_sof_dbg_print_fw_state(sdev, level); 744 sof_ops(sdev)->dbg_dump(sdev, flags); 745 dev_printk(level, sdev->dev, 746 "------------[ DSP dump end ]------------\n"); 747 if (!print_all) 748 sdev->dbg_dump_printed = true; 749 } else if (msg) { 750 dev_printk(level, sdev->dev, "%s\n", msg); 751 } 752 } 753 EXPORT_SYMBOL(snd_sof_dsp_dbg_dump); 754 755 static void snd_sof_ipc_dump(struct snd_sof_dev *sdev) 756 { 757 if (sof_ops(sdev)->ipc_dump && !sdev->ipc_dump_printed) { 758 dev_err(sdev->dev, "------------[ IPC dump start ]------------\n"); 759 sof_ops(sdev)->ipc_dump(sdev); 760 dev_err(sdev->dev, "------------[ IPC dump end ]------------\n"); 761 if (!sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS)) 762 sdev->ipc_dump_printed = true; 763 } 764 } 765 766 void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev) 767 { 768 if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT) || 769 sof_debug_check_flag(SOF_DBG_RETAIN_CTX)) { 770 /* should we prevent DSP entering D3 ? */ 771 if (!sdev->ipc_dump_printed) 772 dev_info(sdev->dev, 773 "preventing DSP entering D3 state to preserve context\n"); 774 pm_runtime_get_noresume(sdev->dev); 775 } 776 777 /* dump vital information to the logs */ 778 snd_sof_ipc_dump(sdev); 779 snd_sof_dsp_dbg_dump(sdev, "Firmware exception", 780 SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); 781 snd_sof_trace_notify_for_error(sdev); 782 } 783 EXPORT_SYMBOL(snd_sof_handle_fw_exception); 784