aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
diff options
context:
space:
mode:
authorIdo Yariv <ido@wizery.com>2016-08-23 14:44:59 -0400
committerLuca Coelho <luciano.coelho@intel.com>2016-09-19 04:29:33 -0400
commit2b55f43f8e477a123bca4ab35351666479bd7b86 (patch)
tree124be3c4e46e61e4b0ad549b9922b65b894f0242 /drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
parent186cd49a4d9ad1a0d4a7371c2d02a05a8ac53a03 (diff)
iwlwifi: mvm: Add mem debugfs entry
In order to access cached/paged memory, there are a couple of firmware commands (one for UMAC and one for LMAC) that let the host access memory and registers indirectly. Since this is done by the firmware on behalf of the host, even if memory is paged out or cached, the host will retrieve the memory as the firmware sees it (paged out memory will get paged in). Export this mechanism via a debugfs entry for both read and write access. WARNING: This mechanism has no protections at all. Invalid addresses may crash or hang the firmware. Writing to arbitrary memory also comes with no guarantees. Signed-off-by: Ido Yariv <idox.yariv@intel.com> Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c')
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c129
1 files changed, 129 insertions, 0 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
index 540b7c9deaef..539d718df797 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
@@ -1518,6 +1518,132 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256);
1518MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8); 1518MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8);
1519#endif 1519#endif
1520 1520
1521static ssize_t iwl_dbgfs_mem_read(struct file *file, char __user *user_buf,
1522 size_t count, loff_t *ppos)
1523{
1524 struct iwl_mvm *mvm = file->private_data;
1525 struct iwl_dbg_mem_access_cmd cmd = {};
1526 struct iwl_dbg_mem_access_rsp *rsp;
1527 struct iwl_host_cmd hcmd = {
1528 .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
1529 .data = { &cmd, },
1530 .len = { sizeof(cmd) },
1531 };
1532 size_t delta, len;
1533 ssize_t ret;
1534
1535 hcmd.id = iwl_cmd_id(*ppos >> 24 ? UMAC_RD_WR : LMAC_RD_WR,
1536 DEBUG_GROUP, 0);
1537 cmd.op = cpu_to_le32(DEBUG_MEM_OP_READ);
1538
1539 /* Take care of alignment of both the position and the length */
1540 delta = *ppos & 0x3;
1541 cmd.addr = cpu_to_le32(*ppos - delta);
1542 cmd.len = cpu_to_le32(min(ALIGN(count + delta, 4) / 4,
1543 (size_t)DEBUG_MEM_MAX_SIZE_DWORDS));
1544
1545 mutex_lock(&mvm->mutex);
1546 ret = iwl_mvm_send_cmd(mvm, &hcmd);
1547 mutex_unlock(&mvm->mutex);
1548
1549 if (ret < 0)
1550 return ret;
1551
1552 rsp = (void *)hcmd.resp_pkt->data;
1553 if (le32_to_cpu(rsp->status) != DEBUG_MEM_STATUS_SUCCESS) {
1554 ret = -ENXIO;
1555 goto out;
1556 }
1557
1558 len = min((size_t)le32_to_cpu(rsp->len) << 2,
1559 iwl_rx_packet_payload_len(hcmd.resp_pkt) - sizeof(*rsp));
1560 len = min(len - delta, count);
1561 if (len < 0) {
1562 ret = -EFAULT;
1563 goto out;
1564 }
1565
1566 ret = len - copy_to_user(user_buf, (void *)rsp->data + delta, len);
1567 *ppos += ret;
1568
1569out:
1570 iwl_free_resp(&hcmd);
1571 return ret;
1572}
1573
1574static ssize_t iwl_dbgfs_mem_write(struct file *file,
1575 const char __user *user_buf, size_t count,
1576 loff_t *ppos)
1577{
1578 struct iwl_mvm *mvm = file->private_data;
1579 struct iwl_dbg_mem_access_cmd *cmd;
1580 struct iwl_dbg_mem_access_rsp *rsp;
1581 struct iwl_host_cmd hcmd = {};
1582 size_t cmd_size;
1583 size_t data_size;
1584 u32 op, len;
1585 ssize_t ret;
1586
1587 hcmd.id = iwl_cmd_id(*ppos >> 24 ? UMAC_RD_WR : LMAC_RD_WR,
1588 DEBUG_GROUP, 0);
1589
1590 if (*ppos & 0x3 || count < 4) {
1591 op = DEBUG_MEM_OP_WRITE_BYTES;
1592 len = min(count, (size_t)(4 - (*ppos & 0x3)));
1593 data_size = len;
1594 } else {
1595 op = DEBUG_MEM_OP_WRITE;
1596 len = min(count >> 2, (size_t)DEBUG_MEM_MAX_SIZE_DWORDS);
1597 data_size = len << 2;
1598 }
1599
1600 cmd_size = sizeof(*cmd) + ALIGN(data_size, 4);
1601 cmd = kzalloc(cmd_size, GFP_KERNEL);
1602 if (!cmd)
1603 return -ENOMEM;
1604
1605 cmd->op = cpu_to_le32(op);
1606 cmd->len = cpu_to_le32(len);
1607 cmd->addr = cpu_to_le32(*ppos);
1608 if (copy_from_user((void *)cmd->data, user_buf, data_size)) {
1609 kfree(cmd);
1610 return -EFAULT;
1611 }
1612
1613 hcmd.flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
1614 hcmd.data[0] = (void *)cmd;
1615 hcmd.len[0] = cmd_size;
1616
1617 mutex_lock(&mvm->mutex);
1618 ret = iwl_mvm_send_cmd(mvm, &hcmd);
1619 mutex_unlock(&mvm->mutex);
1620
1621 kfree(cmd);
1622
1623 if (ret < 0)
1624 return ret;
1625
1626 rsp = (void *)hcmd.resp_pkt->data;
1627 if (rsp->status != DEBUG_MEM_STATUS_SUCCESS) {
1628 ret = -ENXIO;
1629 goto out;
1630 }
1631
1632 ret = data_size;
1633 *ppos += ret;
1634
1635out:
1636 iwl_free_resp(&hcmd);
1637 return ret;
1638}
1639
1640static const struct file_operations iwl_dbgfs_mem_ops = {
1641 .read = iwl_dbgfs_mem_read,
1642 .write = iwl_dbgfs_mem_write,
1643 .open = simple_open,
1644 .llseek = default_llseek,
1645};
1646
1521int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) 1647int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
1522{ 1648{
1523 struct dentry *bcast_dir __maybe_unused; 1649 struct dentry *bcast_dir __maybe_unused;
@@ -1615,6 +1741,9 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
1615 mvm->debugfs_dir, &mvm->nvm_phy_sku_blob)) 1741 mvm->debugfs_dir, &mvm->nvm_phy_sku_blob))
1616 goto err; 1742 goto err;
1617 1743
1744 debugfs_create_file("mem", S_IRUSR | S_IWUSR, dbgfs_dir, mvm,
1745 &iwl_dbgfs_mem_ops);
1746
1618 /* 1747 /*
1619 * Create a symlink with mac80211. It will be removed when mac80211 1748 * Create a symlink with mac80211. It will be removed when mac80211
1620 * exists (before the opmode exists which removes the target.) 1749 * exists (before the opmode exists which removes the target.)