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_resume_and_get(sdev->dev); 233 if (ret < 0 && ret != -EACCES) { 234 dev_err(sdev->dev, "error: enabling device failed: %d\n", ret); 235 goto error; 236 } 237 238 ret = sof_ipc_tx_message(sdev->ipc, &msg, msg.size, reply, SOF_IPC_MSG_MAX_SIZE); 239 pm_runtime_mark_last_busy(sdev->dev); 240 pm_runtime_put_autosuspend(sdev->dev); 241 if (ret < 0 || reply->rhdr.error < 0) { 242 ret = min(ret, reply->rhdr.error); 243 dev_err(sdev->dev, "error: reading memory info failed, %d\n", ret); 244 goto error; 245 } 246 247 if (struct_size(reply, elems, reply->num_elems) != reply->rhdr.hdr.size) { 248 dev_err(sdev->dev, "error: invalid memory info ipc struct size, %d\n", 249 reply->rhdr.hdr.size); 250 ret = -EINVAL; 251 goto error; 252 } 253 254 for (i = 0, len = 0; i < reply->num_elems; i++) { 255 ret = scnprintf(buf + len, buff_size - len, "zone %d.%d used %#8x free %#8x\n", 256 reply->elems[i].zone, reply->elems[i].id, 257 reply->elems[i].used, reply->elems[i].free); 258 if (ret < 0) 259 goto error; 260 len += ret; 261 } 262 263 ret = len; 264 error: 265 kfree(reply); 266 return ret; 267 } 268 269 static ssize_t memory_info_read(struct file *file, char __user *to, size_t count, loff_t *ppos) 270 { 271 struct snd_sof_dfsentry *dfse = file->private_data; 272 struct snd_sof_dev *sdev = dfse->sdev; 273 int data_length; 274 275 /* read memory info from FW only once for each file read */ 276 if (!*ppos) { 277 dfse->buf_data_size = 0; 278 data_length = memory_info_update(sdev, dfse->buf, dfse->size); 279 if (data_length < 0) 280 return data_length; 281 dfse->buf_data_size = data_length; 282 } 283 284 return simple_read_from_buffer(to, count, ppos, dfse->buf, dfse->buf_data_size); 285 } 286 287 static int memory_info_open(struct inode *inode, struct file *file) 288 { 289 struct snd_sof_dfsentry *dfse = inode->i_private; 290 struct snd_sof_dev *sdev = dfse->sdev; 291 292 file->private_data = dfse; 293 294 /* allocate buffer memory only in first open run, to save memory when unused */ 295 if (!dfse->buf) { 296 dfse->buf = devm_kmalloc(sdev->dev, PAGE_SIZE, GFP_KERNEL); 297 if (!dfse->buf) 298 return -ENOMEM; 299 dfse->size = PAGE_SIZE; 300 } 301 302 return 0; 303 } 304 305 static const struct file_operations memory_info_fops = { 306 .open = memory_info_open, 307 .read = memory_info_read, 308 .llseek = default_llseek, 309 }; 310 311 int snd_sof_dbg_memory_info_init(struct snd_sof_dev *sdev) 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 /* don't allocate buffer before first usage, to save memory when unused */ 320 dfse->type = SOF_DFSENTRY_TYPE_BUF; 321 dfse->sdev = sdev; 322 323 debugfs_create_file("memory_info", 0444, sdev->debugfs_root, dfse, &memory_info_fops); 324 325 /* add to dfsentry list */ 326 list_add(&dfse->list, &sdev->dfsentry_list); 327 return 0; 328 } 329 EXPORT_SYMBOL_GPL(snd_sof_dbg_memory_info_init); 330 331 int snd_sof_dbg_init(struct snd_sof_dev *sdev) 332 { 333 struct snd_sof_dsp_ops *ops = sof_ops(sdev); 334 const struct snd_sof_debugfs_map *map; 335 int i; 336 int err; 337 338 /* use "sof" as top level debugFS dir */ 339 sdev->debugfs_root = debugfs_create_dir("sof", NULL); 340 341 /* init dfsentry list */ 342 INIT_LIST_HEAD(&sdev->dfsentry_list); 343 344 /* create debugFS files for platform specific MMIO/DSP memories */ 345 for (i = 0; i < ops->debug_map_count; i++) { 346 map = &ops->debug_map[i]; 347 348 err = snd_sof_debugfs_io_item(sdev, sdev->bar[map->bar] + 349 map->offset, map->size, 350 map->name, map->access_type); 351 /* errors are only due to memory allocation, not debugfs */ 352 if (err < 0) 353 return err; 354 } 355 356 return snd_sof_debugfs_buf_item(sdev, &sdev->fw_state, 357 sizeof(sdev->fw_state), 358 "fw_state", 0444); 359 } 360 EXPORT_SYMBOL_GPL(snd_sof_dbg_init); 361 362 void snd_sof_free_debug(struct snd_sof_dev *sdev) 363 { 364 debugfs_remove_recursive(sdev->debugfs_root); 365 } 366 EXPORT_SYMBOL_GPL(snd_sof_free_debug); 367 368 static const struct soc_fw_state_info { 369 enum sof_fw_state state; 370 const char *name; 371 } fw_state_dbg[] = { 372 {SOF_FW_BOOT_NOT_STARTED, "SOF_FW_BOOT_NOT_STARTED"}, 373 {SOF_DSPLESS_MODE, "SOF_DSPLESS_MODE"}, 374 {SOF_FW_BOOT_PREPARE, "SOF_FW_BOOT_PREPARE"}, 375 {SOF_FW_BOOT_IN_PROGRESS, "SOF_FW_BOOT_IN_PROGRESS"}, 376 {SOF_FW_BOOT_FAILED, "SOF_FW_BOOT_FAILED"}, 377 {SOF_FW_BOOT_READY_FAILED, "SOF_FW_BOOT_READY_FAILED"}, 378 {SOF_FW_BOOT_READY_OK, "SOF_FW_BOOT_READY_OK"}, 379 {SOF_FW_BOOT_COMPLETE, "SOF_FW_BOOT_COMPLETE"}, 380 {SOF_FW_CRASHED, "SOF_FW_CRASHED"}, 381 }; 382 383 static void snd_sof_dbg_print_fw_state(struct snd_sof_dev *sdev, const char *level) 384 { 385 int i; 386 387 for (i = 0; i < ARRAY_SIZE(fw_state_dbg); i++) { 388 if (sdev->fw_state == fw_state_dbg[i].state) { 389 dev_printk(level, sdev->dev, "fw_state: %s (%d)\n", 390 fw_state_dbg[i].name, i); 391 return; 392 } 393 } 394 395 dev_printk(level, sdev->dev, "fw_state: UNKNOWN (%d)\n", sdev->fw_state); 396 } 397 398 void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, const char *msg, u32 flags) 399 { 400 char *level = (flags & SOF_DBG_DUMP_OPTIONAL) ? KERN_DEBUG : KERN_ERR; 401 bool print_all = sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS); 402 403 if (flags & SOF_DBG_DUMP_OPTIONAL && !print_all) 404 return; 405 406 if (sof_ops(sdev)->dbg_dump && !sdev->dbg_dump_printed) { 407 dev_printk(level, sdev->dev, 408 "------------[ DSP dump start ]------------\n"); 409 if (msg) 410 dev_printk(level, sdev->dev, "%s\n", msg); 411 snd_sof_dbg_print_fw_state(sdev, level); 412 sof_ops(sdev)->dbg_dump(sdev, flags); 413 dev_printk(level, sdev->dev, 414 "------------[ DSP dump end ]------------\n"); 415 if (!print_all) 416 sdev->dbg_dump_printed = true; 417 } else if (msg) { 418 dev_printk(level, sdev->dev, "%s\n", msg); 419 } 420 } 421 EXPORT_SYMBOL(snd_sof_dsp_dbg_dump); 422 423 static void snd_sof_ipc_dump(struct snd_sof_dev *sdev) 424 { 425 if (sof_ops(sdev)->ipc_dump && !sdev->ipc_dump_printed) { 426 dev_err(sdev->dev, "------------[ IPC dump start ]------------\n"); 427 sof_ops(sdev)->ipc_dump(sdev); 428 dev_err(sdev->dev, "------------[ IPC dump end ]------------\n"); 429 if (!sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS)) 430 sdev->ipc_dump_printed = true; 431 } 432 } 433 434 void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev, const char *msg) 435 { 436 if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT) || 437 sof_debug_check_flag(SOF_DBG_RETAIN_CTX)) { 438 /* should we prevent DSP entering D3 ? */ 439 if (!sdev->ipc_dump_printed) 440 dev_info(sdev->dev, 441 "Attempting to prevent DSP from entering D3 state to preserve context\n"); 442 pm_runtime_get_if_in_use(sdev->dev); 443 } 444 445 /* dump vital information to the logs */ 446 snd_sof_ipc_dump(sdev); 447 snd_sof_dsp_dbg_dump(sdev, msg, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); 448 sof_fw_trace_fw_crashed(sdev); 449 } 450 EXPORT_SYMBOL(snd_sof_handle_fw_exception); 451