aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/intel
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
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')
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c129
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h50
2 files changed, 179 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.)
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h
index 2f92994d0e5b..fdd9506e10cd 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h
@@ -340,6 +340,11 @@ enum iwl_prot_offload_subcmd_ids {
340 STORED_BEACON_NTF = 0xFF, 340 STORED_BEACON_NTF = 0xFF,
341}; 341};
342 342
343enum iwl_fmac_debug_cmds {
344 LMAC_RD_WR = 0x0,
345 UMAC_RD_WR = 0x1,
346};
347
343/* command groups */ 348/* command groups */
344enum { 349enum {
345 LEGACY_GROUP = 0x0, 350 LEGACY_GROUP = 0x0,
@@ -349,6 +354,7 @@ enum {
349 PHY_OPS_GROUP = 0x4, 354 PHY_OPS_GROUP = 0x4,
350 DATA_PATH_GROUP = 0x5, 355 DATA_PATH_GROUP = 0x5,
351 PROT_OFFLOAD_GROUP = 0xb, 356 PROT_OFFLOAD_GROUP = 0xb,
357 DEBUG_GROUP = 0xf,
352}; 358};
353 359
354/** 360/**
@@ -2149,4 +2155,48 @@ struct iwl_channel_switch_noa_notif {
2149 __le32 id_and_color; 2155 __le32 id_and_color;
2150} __packed; /* CHANNEL_SWITCH_START_NTFY_API_S_VER_1 */ 2156} __packed; /* CHANNEL_SWITCH_START_NTFY_API_S_VER_1 */
2151 2157
2158/* Operation types for the debug mem access */
2159enum {
2160 DEBUG_MEM_OP_READ = 0,
2161 DEBUG_MEM_OP_WRITE = 1,
2162 DEBUG_MEM_OP_WRITE_BYTES = 2,
2163};
2164
2165#define DEBUG_MEM_MAX_SIZE_DWORDS 32
2166
2167/**
2168 * struct iwl_dbg_mem_access_cmd - Request the device to read/write memory
2169 * @op: DEBUG_MEM_OP_*
2170 * @addr: address to read/write from/to
2171 * @len: in dwords, to read/write
2172 * @data: for write opeations, contains the source buffer
2173 */
2174struct iwl_dbg_mem_access_cmd {
2175 __le32 op;
2176 __le32 addr;
2177 __le32 len;
2178 __le32 data[];
2179} __packed; /* DEBUG_(U|L)MAC_RD_WR_CMD_API_S_VER_1 */
2180
2181/* Status responses for the debug mem access */
2182enum {
2183 DEBUG_MEM_STATUS_SUCCESS = 0x0,
2184 DEBUG_MEM_STATUS_FAILED = 0x1,
2185 DEBUG_MEM_STATUS_LOCKED = 0x2,
2186 DEBUG_MEM_STATUS_HIDDEN = 0x3,
2187 DEBUG_MEM_STATUS_LENGTH = 0x4,
2188};
2189
2190/**
2191 * struct iwl_dbg_mem_access_rsp - Response to debug mem commands
2192 * @status: DEBUG_MEM_STATUS_*
2193 * @len: read dwords (0 for write operations)
2194 * @data: contains the read DWs
2195 */
2196struct iwl_dbg_mem_access_rsp {
2197 __le32 status;
2198 __le32 len;
2199 __le32 data[];
2200} __packed; /* DEBUG_(U|L)MAC_RD_WR_RSP_API_S_VER_1 */
2201
2152#endif /* __fw_api_h__ */ 2202#endif /* __fw_api_h__ */