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 static ssize_t sof_dfsentry_write(struct file *file, const char __user *buffer, 23 size_t count, loff_t *ppos) 24 { 25 size_t size; 26 char *string; 27 int ret; 28 29 string = kzalloc(count+1, GFP_KERNEL); 30 if (!string) 31 return -ENOMEM; 32 33 size = simple_write_to_buffer(string, count, ppos, buffer, count); 34 ret = size; 35 36 kfree(string); 37 return ret; 38 } 39 40 static ssize_t sof_dfsentry_read(struct file *file, char __user *buffer, 41 size_t count, loff_t *ppos) 42 { 43 struct snd_sof_dfsentry *dfse = file->private_data; 44 struct snd_sof_dev *sdev = dfse->sdev; 45 loff_t pos = *ppos; 46 size_t size_ret; 47 int skip = 0; 48 int size; 49 u8 *buf; 50 51 size = dfse->size; 52 53 /* validate position & count */ 54 if (pos < 0) 55 return -EINVAL; 56 if (pos >= size || !count) 57 return 0; 58 /* find the minimum. min() is not used since it adds sparse warnings */ 59 if (count > size - pos) 60 count = size - pos; 61 62 /* align io read start to u32 multiple */ 63 pos = ALIGN_DOWN(pos, 4); 64 65 /* intermediate buffer size must be u32 multiple */ 66 size = ALIGN(count, 4); 67 68 /* if start position is unaligned, read extra u32 */ 69 if (unlikely(pos != *ppos)) { 70 skip = *ppos - pos; 71 if (pos + size + 4 < dfse->size) 72 size += 4; 73 } 74 75 buf = kzalloc(size, GFP_KERNEL); 76 if (!buf) 77 return -ENOMEM; 78 79 if (dfse->type == SOF_DFSENTRY_TYPE_IOMEM) { 80 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) 81 /* 82 * If the DSP is active: copy from IO. 83 * If the DSP is suspended: 84 * - Copy from IO if the memory is always accessible. 85 * - Otherwise, copy from cached buffer. 86 */ 87 if (pm_runtime_active(sdev->dev) || 88 dfse->access_type == SOF_DEBUGFS_ACCESS_ALWAYS) { 89 memcpy_fromio(buf, dfse->io_mem + pos, size); 90 } else { 91 dev_info(sdev->dev, 92 "Copying cached debugfs data\n"); 93 memcpy(buf, dfse->cache_buf + pos, size); 94 } 95 #else 96 /* if the DSP is in D3 */ 97 if (!pm_runtime_active(sdev->dev) && 98 dfse->access_type == SOF_DEBUGFS_ACCESS_D0_ONLY) { 99 dev_err(sdev->dev, 100 "error: debugfs entry cannot be read in DSP D3\n"); 101 kfree(buf); 102 return -EINVAL; 103 } 104 105 memcpy_fromio(buf, dfse->io_mem + pos, size); 106 #endif 107 } else { 108 memcpy(buf, ((u8 *)(dfse->buf) + pos), size); 109 } 110 111 /* copy to userspace */ 112 size_ret = copy_to_user(buffer, buf + skip, count); 113 114 kfree(buf); 115 116 /* update count & position if copy succeeded */ 117 if (size_ret) 118 return -EFAULT; 119 120 *ppos = pos + count; 121 122 return count; 123 } 124 125 static const struct file_operations sof_dfs_fops = { 126 .open = simple_open, 127 .read = sof_dfsentry_read, 128 .llseek = default_llseek, 129 .write = sof_dfsentry_write, 130 }; 131 132 /* create FS entry for debug files that can expose DSP memories, registers */ 133 static int snd_sof_debugfs_io_item(struct snd_sof_dev *sdev, 134 void __iomem *base, size_t size, 135 const char *name, 136 enum sof_debugfs_access_type access_type) 137 { 138 struct snd_sof_dfsentry *dfse; 139 140 if (!sdev) 141 return -EINVAL; 142 143 dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); 144 if (!dfse) 145 return -ENOMEM; 146 147 dfse->type = SOF_DFSENTRY_TYPE_IOMEM; 148 dfse->io_mem = base; 149 dfse->size = size; 150 dfse->sdev = sdev; 151 dfse->access_type = access_type; 152 153 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) 154 /* 155 * allocate cache buffer that will be used to save the mem window 156 * contents prior to suspend 157 */ 158 if (access_type == SOF_DEBUGFS_ACCESS_D0_ONLY) { 159 dfse->cache_buf = devm_kzalloc(sdev->dev, size, GFP_KERNEL); 160 if (!dfse->cache_buf) 161 return -ENOMEM; 162 } 163 #endif 164 165 debugfs_create_file(name, 0444, sdev->debugfs_root, dfse, 166 &sof_dfs_fops); 167 168 /* add to dfsentry list */ 169 list_add(&dfse->list, &sdev->dfsentry_list); 170 171 return 0; 172 } 173 174 int snd_sof_debugfs_add_region_item_iomem(struct snd_sof_dev *sdev, 175 enum snd_sof_fw_blk_type blk_type, u32 offset, 176 size_t size, const char *name, 177 enum sof_debugfs_access_type access_type) 178 { 179 int bar = snd_sof_dsp_get_bar_index(sdev, blk_type); 180 181 if (bar < 0) 182 return bar; 183 184 return snd_sof_debugfs_io_item(sdev, sdev->bar[bar] + offset, size, name, 185 access_type); 186 } 187 EXPORT_SYMBOL_GPL(snd_sof_debugfs_add_region_item_iomem); 188 189 /* create FS entry for debug files to expose kernel memory */ 190 int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev, 191 void *base, size_t size, 192 const char *name, mode_t mode) 193 { 194 struct snd_sof_dfsentry *dfse; 195 196 if (!sdev) 197 return -EINVAL; 198 199 dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); 200 if (!dfse) 201 return -ENOMEM; 202 203 dfse->type = SOF_DFSENTRY_TYPE_BUF; 204 dfse->buf = base; 205 dfse->size = size; 206 dfse->sdev = sdev; 207 208 debugfs_create_file(name, mode, sdev->debugfs_root, dfse, 209 &sof_dfs_fops); 210 /* add to dfsentry list */ 211 list_add(&dfse->list, &sdev->dfsentry_list); 212 213 return 0; 214 } 215 EXPORT_SYMBOL_GPL(snd_sof_debugfs_buf_item); 216 217 static int memory_info_update(struct snd_sof_dev *sdev, char *buf, size_t buff_size) 218 { 219 struct sof_ipc_cmd_hdr msg = { 220 .size = sizeof(struct sof_ipc_cmd_hdr), 221 .cmd = SOF_IPC_GLB_DEBUG | SOF_IPC_DEBUG_MEM_USAGE, 222 }; 223 struct sof_ipc_dbg_mem_usage *reply; 224 int len; 225 int ret; 226 int i; 227 228 reply = kmalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); 229 if (!reply) 230 return -ENOMEM; 231 232 ret = pm_runtime_get_sync(sdev->dev); 233 if (ret < 0 && ret != -EACCES) { 234 pm_runtime_put_noidle(sdev->dev); 235 dev_err(sdev->dev, "error: enabling device failed: %d\n", ret); 236 goto error; 237 } 238 239 ret = sof_ipc_tx_message(sdev->ipc, msg.cmd, &msg, msg.size, reply, SOF_IPC_MSG_MAX_SIZE); 240 pm_runtime_mark_last_busy(sdev->dev); 241 pm_runtime_put_autosuspend(sdev->dev); 242 if (ret < 0 || reply->rhdr.error < 0) { 243 ret = min(ret, reply->rhdr.error); 244 dev_err(sdev->dev, "error: reading memory info failed, %d\n", ret); 245 goto error; 246 } 247 248 if (struct_size(reply, elems, reply->num_elems) != reply->rhdr.hdr.size) { 249 dev_err(sdev->dev, "error: invalid memory info ipc struct size, %d\n", 250 reply->rhdr.hdr.size); 251 ret = -EINVAL; 252 goto error; 253 } 254 255 for (i = 0, len = 0; i < reply->num_elems; i++) { 256 ret = snprintf(buf + len, buff_size - len, "zone %d.%d used %#8x free %#8x\n", 257 reply->elems[i].zone, reply->elems[i].id, 258 reply->elems[i].used, reply->elems[i].free); 259 if (ret < 0) 260 goto error; 261 len += ret; 262 } 263 264 ret = len; 265 error: 266 kfree(reply); 267 return ret; 268 } 269 270 static ssize_t memory_info_read(struct file *file, char __user *to, size_t count, loff_t *ppos) 271 { 272 struct snd_sof_dfsentry *dfse = file->private_data; 273 struct snd_sof_dev *sdev = dfse->sdev; 274 int data_length; 275 276 /* read memory info from FW only once for each file read */ 277 if (!*ppos) { 278 dfse->buf_data_size = 0; 279 data_length = memory_info_update(sdev, dfse->buf, dfse->size); 280 if (data_length < 0) 281 return data_length; 282 dfse->buf_data_size = data_length; 283 } 284 285 return simple_read_from_buffer(to, count, ppos, dfse->buf, dfse->buf_data_size); 286 } 287 288 static int memory_info_open(struct inode *inode, struct file *file) 289 { 290 struct snd_sof_dfsentry *dfse = inode->i_private; 291 struct snd_sof_dev *sdev = dfse->sdev; 292 293 file->private_data = dfse; 294 295 /* allocate buffer memory only in first open run, to save memory when unused */ 296 if (!dfse->buf) { 297 dfse->buf = devm_kmalloc(sdev->dev, PAGE_SIZE, GFP_KERNEL); 298 if (!dfse->buf) 299 return -ENOMEM; 300 dfse->size = PAGE_SIZE; 301 } 302 303 return 0; 304 } 305 306 static const struct file_operations memory_info_fops = { 307 .open = memory_info_open, 308 .read = memory_info_read, 309 .llseek = default_llseek, 310 }; 311 312 int snd_sof_dbg_memory_info_init(struct snd_sof_dev *sdev) 313 { 314 struct snd_sof_dfsentry *dfse; 315 316 dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); 317 if (!dfse) 318 return -ENOMEM; 319 320 /* don't allocate buffer before first usage, to save memory when unused */ 321 dfse->type = SOF_DFSENTRY_TYPE_BUF; 322 dfse->sdev = sdev; 323 324 debugfs_create_file("memory_info", 0444, sdev->debugfs_root, dfse, &memory_info_fops); 325 326 /* add to dfsentry list */ 327 list_add(&dfse->list, &sdev->dfsentry_list); 328 return 0; 329 } 330 EXPORT_SYMBOL_GPL(snd_sof_dbg_memory_info_init); 331 332 int snd_sof_dbg_init(struct snd_sof_dev *sdev) 333 { 334 const struct snd_sof_dsp_ops *ops = sof_ops(sdev); 335 const struct snd_sof_debugfs_map *map; 336 int i; 337 int err; 338 339 /* use "sof" as top level debugFS dir */ 340 sdev->debugfs_root = debugfs_create_dir("sof", NULL); 341 342 /* init dfsentry list */ 343 INIT_LIST_HEAD(&sdev->dfsentry_list); 344 345 /* create debugFS files for platform specific MMIO/DSP memories */ 346 for (i = 0; i < ops->debug_map_count; i++) { 347 map = &ops->debug_map[i]; 348 349 err = snd_sof_debugfs_io_item(sdev, sdev->bar[map->bar] + 350 map->offset, map->size, 351 map->name, map->access_type); 352 /* errors are only due to memory allocation, not debugfs */ 353 if (err < 0) 354 return err; 355 } 356 357 return 0; 358 } 359 EXPORT_SYMBOL_GPL(snd_sof_dbg_init); 360 361 void snd_sof_free_debug(struct snd_sof_dev *sdev) 362 { 363 debugfs_remove_recursive(sdev->debugfs_root); 364 } 365 EXPORT_SYMBOL_GPL(snd_sof_free_debug); 366 367 static const struct soc_fw_state_info { 368 enum sof_fw_state state; 369 const char *name; 370 } fw_state_dbg[] = { 371 {SOF_FW_BOOT_NOT_STARTED, "SOF_FW_BOOT_NOT_STARTED"}, 372 {SOF_FW_BOOT_PREPARE, "SOF_FW_BOOT_PREPARE"}, 373 {SOF_FW_BOOT_IN_PROGRESS, "SOF_FW_BOOT_IN_PROGRESS"}, 374 {SOF_FW_BOOT_FAILED, "SOF_FW_BOOT_FAILED"}, 375 {SOF_FW_BOOT_READY_FAILED, "SOF_FW_BOOT_READY_FAILED"}, 376 {SOF_FW_BOOT_READY_OK, "SOF_FW_BOOT_READY_OK"}, 377 {SOF_FW_BOOT_COMPLETE, "SOF_FW_BOOT_COMPLETE"}, 378 {SOF_FW_CRASHED, "SOF_FW_CRASHED"}, 379 }; 380 381 static void snd_sof_dbg_print_fw_state(struct snd_sof_dev *sdev, const char *level) 382 { 383 int i; 384 385 for (i = 0; i < ARRAY_SIZE(fw_state_dbg); i++) { 386 if (sdev->fw_state == fw_state_dbg[i].state) { 387 dev_printk(level, sdev->dev, "fw_state: %s (%d)\n", 388 fw_state_dbg[i].name, i); 389 return; 390 } 391 } 392 393 dev_printk(level, sdev->dev, "fw_state: UNKNOWN (%d)\n", sdev->fw_state); 394 } 395 396 void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, const char *msg, u32 flags) 397 { 398 char *level = (flags & SOF_DBG_DUMP_OPTIONAL) ? KERN_DEBUG : KERN_ERR; 399 bool print_all = sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS); 400 401 if (flags & SOF_DBG_DUMP_OPTIONAL && !print_all) 402 return; 403 404 if (sof_ops(sdev)->dbg_dump && !sdev->dbg_dump_printed) { 405 dev_printk(level, sdev->dev, 406 "------------[ DSP dump start ]------------\n"); 407 if (msg) 408 dev_printk(level, sdev->dev, "%s\n", msg); 409 snd_sof_dbg_print_fw_state(sdev, level); 410 sof_ops(sdev)->dbg_dump(sdev, flags); 411 dev_printk(level, sdev->dev, 412 "------------[ DSP dump end ]------------\n"); 413 if (!print_all) 414 sdev->dbg_dump_printed = true; 415 } else if (msg) { 416 dev_printk(level, sdev->dev, "%s\n", msg); 417 } 418 } 419 EXPORT_SYMBOL(snd_sof_dsp_dbg_dump); 420 421 static void snd_sof_ipc_dump(struct snd_sof_dev *sdev) 422 { 423 if (sof_ops(sdev)->ipc_dump && !sdev->ipc_dump_printed) { 424 dev_err(sdev->dev, "------------[ IPC dump start ]------------\n"); 425 sof_ops(sdev)->ipc_dump(sdev); 426 dev_err(sdev->dev, "------------[ IPC dump end ]------------\n"); 427 if (!sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS)) 428 sdev->ipc_dump_printed = true; 429 } 430 } 431 432 void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev) 433 { 434 if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT) || 435 sof_debug_check_flag(SOF_DBG_RETAIN_CTX)) { 436 /* should we prevent DSP entering D3 ? */ 437 if (!sdev->ipc_dump_printed) 438 dev_info(sdev->dev, 439 "preventing DSP entering D3 state to preserve context\n"); 440 pm_runtime_get_noresume(sdev->dev); 441 } 442 443 /* dump vital information to the logs */ 444 snd_sof_ipc_dump(sdev); 445 snd_sof_dsp_dbg_dump(sdev, "Firmware exception", 446 SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); 447 snd_sof_trace_notify_for_error(sdev); 448 } 449 EXPORT_SYMBOL(snd_sof_handle_fw_exception); 450