1 /****************************************************************************** 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 * GPL LICENSE SUMMARY 7 * 8 * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH 9 * Copyright(c) 2016 - 2017 Intel Deutschland GmbH 10 * Copyright(c) 2012 - 2014, 2018 - 2020 Intel Corporation 11 * 12 * This program is free software; you can redistribute it and/or modify 13 * it under the terms of version 2 of the GNU General Public License as 14 * published by the Free Software Foundation. 15 * 16 * This program is distributed in the hope that it will be useful, but 17 * WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 * General Public License for more details. 20 * 21 * The full GNU General Public License is included in this distribution 22 * in the file called COPYING. 23 * 24 * Contact Information: 25 * Intel Linux Wireless <linuxwifi@intel.com> 26 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 27 * 28 * BSD LICENSE 29 * 30 * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH 31 * Copyright(c) 2016 - 2017 Intel Deutschland GmbH 32 * Copyright(c) 2012 - 2014, 2018 - 2020 Intel Corporation 33 * All rights reserved. 34 * 35 * Redistribution and use in source and binary forms, with or without 36 * modification, are permitted provided that the following conditions 37 * are met: 38 * 39 * * Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * * Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in 43 * the documentation and/or other materials provided with the 44 * distribution. 45 * * Neither the name Intel Corporation nor the names of its 46 * contributors may be used to endorse or promote products derived 47 * from this software without specific prior written permission. 48 * 49 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 50 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 51 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 52 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 53 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 54 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 55 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 56 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 57 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 58 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 59 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 60 * 61 *****************************************************************************/ 62 #include "api/commands.h" 63 #include "debugfs.h" 64 #include "dbg.h" 65 #include <linux/seq_file.h> 66 67 #define FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \ 68 struct dbgfs_##name##_data { \ 69 argtype *arg; \ 70 bool read_done; \ 71 ssize_t rlen; \ 72 char rbuf[buflen]; \ 73 }; \ 74 static int _iwl_dbgfs_##name##_open(struct inode *inode, \ 75 struct file *file) \ 76 { \ 77 struct dbgfs_##name##_data *data; \ 78 \ 79 data = kzalloc(sizeof(*data), GFP_KERNEL); \ 80 if (!data) \ 81 return -ENOMEM; \ 82 \ 83 data->read_done = false; \ 84 data->arg = inode->i_private; \ 85 file->private_data = data; \ 86 \ 87 return 0; \ 88 } 89 90 #define FWRT_DEBUGFS_READ_WRAPPER(name) \ 91 static ssize_t _iwl_dbgfs_##name##_read(struct file *file, \ 92 char __user *user_buf, \ 93 size_t count, loff_t *ppos) \ 94 { \ 95 struct dbgfs_##name##_data *data = file->private_data; \ 96 \ 97 if (!data->read_done) { \ 98 data->read_done = true; \ 99 data->rlen = iwl_dbgfs_##name##_read(data->arg, \ 100 sizeof(data->rbuf),\ 101 data->rbuf); \ 102 } \ 103 \ 104 if (data->rlen < 0) \ 105 return data->rlen; \ 106 return simple_read_from_buffer(user_buf, count, ppos, \ 107 data->rbuf, data->rlen); \ 108 } 109 110 static int _iwl_dbgfs_release(struct inode *inode, struct file *file) 111 { 112 kfree(file->private_data); 113 114 return 0; 115 } 116 117 #define _FWRT_DEBUGFS_READ_FILE_OPS(name, buflen, argtype) \ 118 FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \ 119 FWRT_DEBUGFS_READ_WRAPPER(name) \ 120 static const struct file_operations iwl_dbgfs_##name##_ops = { \ 121 .read = _iwl_dbgfs_##name##_read, \ 122 .open = _iwl_dbgfs_##name##_open, \ 123 .llseek = generic_file_llseek, \ 124 .release = _iwl_dbgfs_release, \ 125 } 126 127 #define FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ 128 static ssize_t _iwl_dbgfs_##name##_write(struct file *file, \ 129 const char __user *user_buf, \ 130 size_t count, loff_t *ppos) \ 131 { \ 132 argtype *arg = \ 133 ((struct dbgfs_##name##_data *)file->private_data)->arg;\ 134 char buf[buflen] = {}; \ 135 size_t buf_size = min(count, sizeof(buf) - 1); \ 136 \ 137 if (copy_from_user(buf, user_buf, buf_size)) \ 138 return -EFAULT; \ 139 \ 140 return iwl_dbgfs_##name##_write(arg, buf, buf_size); \ 141 } 142 143 #define _FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, buflen, argtype) \ 144 FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \ 145 FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ 146 FWRT_DEBUGFS_READ_WRAPPER(name) \ 147 static const struct file_operations iwl_dbgfs_##name##_ops = { \ 148 .write = _iwl_dbgfs_##name##_write, \ 149 .read = _iwl_dbgfs_##name##_read, \ 150 .open = _iwl_dbgfs_##name##_open, \ 151 .llseek = generic_file_llseek, \ 152 .release = _iwl_dbgfs_release, \ 153 } 154 155 #define _FWRT_DEBUGFS_WRITE_FILE_OPS(name, buflen, argtype) \ 156 FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \ 157 FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ 158 static const struct file_operations iwl_dbgfs_##name##_ops = { \ 159 .write = _iwl_dbgfs_##name##_write, \ 160 .open = _iwl_dbgfs_##name##_open, \ 161 .llseek = generic_file_llseek, \ 162 .release = _iwl_dbgfs_release, \ 163 } 164 165 #define FWRT_DEBUGFS_READ_FILE_OPS(name, bufsz) \ 166 _FWRT_DEBUGFS_READ_FILE_OPS(name, bufsz, struct iwl_fw_runtime) 167 168 #define FWRT_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ 169 _FWRT_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct iwl_fw_runtime) 170 171 #define FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ 172 _FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_fw_runtime) 173 174 #define FWRT_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) do { \ 175 debugfs_create_file(alias, mode, parent, fwrt, \ 176 &iwl_dbgfs_##name##_ops); \ 177 } while (0) 178 #define FWRT_DEBUGFS_ADD_FILE(name, parent, mode) \ 179 FWRT_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) 180 181 static int iwl_fw_send_timestamp_marker_cmd(struct iwl_fw_runtime *fwrt) 182 { 183 struct iwl_mvm_marker marker = { 184 .dw_len = sizeof(struct iwl_mvm_marker) / 4, 185 .marker_id = MARKER_ID_SYNC_CLOCK, 186 187 /* the real timestamp is taken from the ftrace clock 188 * this is for finding the match between fw and kernel logs 189 */ 190 .timestamp = cpu_to_le64(fwrt->timestamp.seq++), 191 }; 192 193 struct iwl_host_cmd hcmd = { 194 .id = MARKER_CMD, 195 .flags = CMD_ASYNC, 196 .data[0] = &marker, 197 .len[0] = sizeof(marker), 198 }; 199 200 return iwl_trans_send_cmd(fwrt->trans, &hcmd); 201 } 202 203 static void iwl_fw_timestamp_marker_wk(struct work_struct *work) 204 { 205 int ret; 206 struct iwl_fw_runtime *fwrt = 207 container_of(work, struct iwl_fw_runtime, timestamp.wk.work); 208 unsigned long delay = fwrt->timestamp.delay; 209 210 ret = iwl_fw_send_timestamp_marker_cmd(fwrt); 211 if (!ret && delay) 212 schedule_delayed_work(&fwrt->timestamp.wk, 213 round_jiffies_relative(delay)); 214 else 215 IWL_INFO(fwrt, 216 "stopping timestamp_marker, ret: %d, delay: %u\n", 217 ret, jiffies_to_msecs(delay) / 1000); 218 } 219 220 void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt, u32 delay) 221 { 222 IWL_INFO(fwrt, 223 "starting timestamp_marker trigger with delay: %us\n", 224 delay); 225 226 iwl_fw_cancel_timestamp(fwrt); 227 228 fwrt->timestamp.delay = msecs_to_jiffies(delay * 1000); 229 230 schedule_delayed_work(&fwrt->timestamp.wk, 231 round_jiffies_relative(fwrt->timestamp.delay)); 232 } 233 234 static ssize_t iwl_dbgfs_timestamp_marker_write(struct iwl_fw_runtime *fwrt, 235 char *buf, size_t count) 236 { 237 int ret; 238 u32 delay; 239 240 ret = kstrtou32(buf, 10, &delay); 241 if (ret < 0) 242 return ret; 243 244 iwl_fw_trigger_timestamp(fwrt, delay); 245 246 return count; 247 } 248 249 static ssize_t iwl_dbgfs_timestamp_marker_read(struct iwl_fw_runtime *fwrt, 250 size_t size, char *buf) 251 { 252 u32 delay_secs = jiffies_to_msecs(fwrt->timestamp.delay) / 1000; 253 254 return scnprintf(buf, size, "%d\n", delay_secs); 255 } 256 257 FWRT_DEBUGFS_READ_WRITE_FILE_OPS(timestamp_marker, 16); 258 259 struct hcmd_write_data { 260 __be32 cmd_id; 261 __be32 flags; 262 __be16 length; 263 u8 data[0]; 264 } __packed; 265 266 static ssize_t iwl_dbgfs_send_hcmd_write(struct iwl_fw_runtime *fwrt, char *buf, 267 size_t count) 268 { 269 size_t header_size = (sizeof(u32) * 2 + sizeof(u16)) * 2; 270 size_t data_size = (count - 1) / 2; 271 int ret; 272 struct hcmd_write_data *data; 273 struct iwl_host_cmd hcmd = { 274 .len = { 0, }, 275 .data = { NULL, }, 276 }; 277 278 if (fwrt->ops && fwrt->ops->fw_running && 279 !fwrt->ops->fw_running(fwrt->ops_ctx)) 280 return -EIO; 281 282 if (count < header_size + 1 || count > 1024 * 4) 283 return -EINVAL; 284 285 data = kmalloc(data_size, GFP_KERNEL); 286 if (!data) 287 return -ENOMEM; 288 289 ret = hex2bin((u8 *)data, buf, data_size); 290 if (ret) 291 goto out; 292 293 hcmd.id = be32_to_cpu(data->cmd_id); 294 hcmd.flags = be32_to_cpu(data->flags); 295 hcmd.len[0] = be16_to_cpu(data->length); 296 hcmd.data[0] = data->data; 297 298 if (count != header_size + hcmd.len[0] * 2 + 1) { 299 IWL_ERR(fwrt, 300 "host command data size does not match header length\n"); 301 ret = -EINVAL; 302 goto out; 303 } 304 305 if (fwrt->ops && fwrt->ops->send_hcmd) 306 ret = fwrt->ops->send_hcmd(fwrt->ops_ctx, &hcmd); 307 else 308 ret = -EPERM; 309 310 if (ret < 0) 311 goto out; 312 313 if (hcmd.flags & CMD_WANT_SKB) 314 iwl_free_resp(&hcmd); 315 out: 316 kfree(data); 317 return ret ?: count; 318 } 319 320 FWRT_DEBUGFS_WRITE_FILE_OPS(send_hcmd, 512); 321 322 static ssize_t iwl_dbgfs_fw_dbg_domain_read(struct iwl_fw_runtime *fwrt, 323 size_t size, char *buf) 324 { 325 return scnprintf(buf, size, "0x%08x\n", 326 fwrt->trans->dbg.domains_bitmap); 327 } 328 329 FWRT_DEBUGFS_READ_FILE_OPS(fw_dbg_domain, 20); 330 331 struct iwl_dbgfs_fw_info_priv { 332 struct iwl_fw_runtime *fwrt; 333 }; 334 335 struct iwl_dbgfs_fw_info_state { 336 loff_t pos; 337 }; 338 339 static void *iwl_dbgfs_fw_info_seq_next(struct seq_file *seq, 340 void *v, loff_t *pos) 341 { 342 struct iwl_dbgfs_fw_info_state *state = v; 343 struct iwl_dbgfs_fw_info_priv *priv = seq->private; 344 const struct iwl_fw *fw = priv->fwrt->fw; 345 346 *pos = ++state->pos; 347 if (*pos >= fw->ucode_capa.n_cmd_versions) 348 return NULL; 349 350 return state; 351 } 352 353 static void iwl_dbgfs_fw_info_seq_stop(struct seq_file *seq, 354 void *v) 355 { 356 kfree(v); 357 } 358 359 static void *iwl_dbgfs_fw_info_seq_start(struct seq_file *seq, loff_t *pos) 360 { 361 struct iwl_dbgfs_fw_info_priv *priv = seq->private; 362 const struct iwl_fw *fw = priv->fwrt->fw; 363 struct iwl_dbgfs_fw_info_state *state; 364 365 if (*pos >= fw->ucode_capa.n_cmd_versions) 366 return NULL; 367 368 state = kzalloc(sizeof(*state), GFP_KERNEL); 369 if (!state) 370 return NULL; 371 state->pos = *pos; 372 return state; 373 }; 374 375 static int iwl_dbgfs_fw_info_seq_show(struct seq_file *seq, void *v) 376 { 377 struct iwl_dbgfs_fw_info_state *state = v; 378 struct iwl_dbgfs_fw_info_priv *priv = seq->private; 379 const struct iwl_fw *fw = priv->fwrt->fw; 380 const struct iwl_fw_cmd_version *ver; 381 u32 cmd_id; 382 383 if (!state->pos) 384 seq_puts(seq, "fw_api_ver:\n"); 385 386 ver = &fw->ucode_capa.cmd_versions[state->pos]; 387 388 cmd_id = iwl_cmd_id(ver->cmd, ver->group, 0); 389 390 seq_printf(seq, " 0x%04x:\n", cmd_id); 391 seq_printf(seq, " name: %s\n", 392 iwl_get_cmd_string(priv->fwrt->trans, cmd_id)); 393 seq_printf(seq, " cmd_ver: %d\n", ver->cmd_ver); 394 seq_printf(seq, " notif_ver: %d\n", ver->notif_ver); 395 return 0; 396 } 397 398 static const struct seq_operations iwl_dbgfs_info_seq_ops = { 399 .start = iwl_dbgfs_fw_info_seq_start, 400 .next = iwl_dbgfs_fw_info_seq_next, 401 .stop = iwl_dbgfs_fw_info_seq_stop, 402 .show = iwl_dbgfs_fw_info_seq_show, 403 }; 404 405 static int iwl_dbgfs_fw_info_open(struct inode *inode, struct file *filp) 406 { 407 struct iwl_dbgfs_fw_info_priv *priv; 408 409 priv = __seq_open_private(filp, &iwl_dbgfs_info_seq_ops, 410 sizeof(*priv)); 411 412 if (!priv) 413 return -ENOMEM; 414 415 priv->fwrt = inode->i_private; 416 return 0; 417 } 418 419 static const struct file_operations iwl_dbgfs_fw_info_ops = { 420 .owner = THIS_MODULE, 421 .open = iwl_dbgfs_fw_info_open, 422 .read = seq_read, 423 .llseek = seq_lseek, 424 .release = seq_release_private, 425 }; 426 427 void iwl_fwrt_dbgfs_register(struct iwl_fw_runtime *fwrt, 428 struct dentry *dbgfs_dir) 429 { 430 INIT_DELAYED_WORK(&fwrt->timestamp.wk, iwl_fw_timestamp_marker_wk); 431 FWRT_DEBUGFS_ADD_FILE(timestamp_marker, dbgfs_dir, 0200); 432 FWRT_DEBUGFS_ADD_FILE(fw_info, dbgfs_dir, 0200); 433 FWRT_DEBUGFS_ADD_FILE(send_hcmd, dbgfs_dir, 0200); 434 FWRT_DEBUGFS_ADD_FILE(fw_dbg_domain, dbgfs_dir, 0400); 435 } 436