Skip to content

Commit

Permalink
iwlwifi: mvm: Add mem debugfs entry
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Ido Yariv authored and Luca Coelho committed Sep 19, 2016
1 parent 186cd49 commit 2b55f43
Show file tree
Hide file tree
Showing 2 changed files with 179 additions and 0 deletions.
129 changes: 129 additions & 0 deletions drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -1518,6 +1518,132 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8);
#endif

static ssize_t iwl_dbgfs_mem_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct iwl_mvm *mvm = file->private_data;
struct iwl_dbg_mem_access_cmd cmd = {};
struct iwl_dbg_mem_access_rsp *rsp;
struct iwl_host_cmd hcmd = {
.flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
.data = { &cmd, },
.len = { sizeof(cmd) },
};
size_t delta, len;
ssize_t ret;

hcmd.id = iwl_cmd_id(*ppos >> 24 ? UMAC_RD_WR : LMAC_RD_WR,
DEBUG_GROUP, 0);
cmd.op = cpu_to_le32(DEBUG_MEM_OP_READ);

/* Take care of alignment of both the position and the length */
delta = *ppos & 0x3;
cmd.addr = cpu_to_le32(*ppos - delta);
cmd.len = cpu_to_le32(min(ALIGN(count + delta, 4) / 4,
(size_t)DEBUG_MEM_MAX_SIZE_DWORDS));

mutex_lock(&mvm->mutex);
ret = iwl_mvm_send_cmd(mvm, &hcmd);
mutex_unlock(&mvm->mutex);

if (ret < 0)
return ret;

rsp = (void *)hcmd.resp_pkt->data;
if (le32_to_cpu(rsp->status) != DEBUG_MEM_STATUS_SUCCESS) {
ret = -ENXIO;
goto out;
}

len = min((size_t)le32_to_cpu(rsp->len) << 2,
iwl_rx_packet_payload_len(hcmd.resp_pkt) - sizeof(*rsp));
len = min(len - delta, count);
if (len < 0) {
ret = -EFAULT;
goto out;
}

ret = len - copy_to_user(user_buf, (void *)rsp->data + delta, len);
*ppos += ret;

out:
iwl_free_resp(&hcmd);
return ret;
}

static ssize_t iwl_dbgfs_mem_write(struct file *file,
const char __user *user_buf, size_t count,
loff_t *ppos)
{
struct iwl_mvm *mvm = file->private_data;
struct iwl_dbg_mem_access_cmd *cmd;
struct iwl_dbg_mem_access_rsp *rsp;
struct iwl_host_cmd hcmd = {};
size_t cmd_size;
size_t data_size;
u32 op, len;
ssize_t ret;

hcmd.id = iwl_cmd_id(*ppos >> 24 ? UMAC_RD_WR : LMAC_RD_WR,
DEBUG_GROUP, 0);

if (*ppos & 0x3 || count < 4) {
op = DEBUG_MEM_OP_WRITE_BYTES;
len = min(count, (size_t)(4 - (*ppos & 0x3)));
data_size = len;
} else {
op = DEBUG_MEM_OP_WRITE;
len = min(count >> 2, (size_t)DEBUG_MEM_MAX_SIZE_DWORDS);
data_size = len << 2;
}

cmd_size = sizeof(*cmd) + ALIGN(data_size, 4);
cmd = kzalloc(cmd_size, GFP_KERNEL);
if (!cmd)
return -ENOMEM;

cmd->op = cpu_to_le32(op);
cmd->len = cpu_to_le32(len);
cmd->addr = cpu_to_le32(*ppos);
if (copy_from_user((void *)cmd->data, user_buf, data_size)) {
kfree(cmd);
return -EFAULT;
}

hcmd.flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
hcmd.data[0] = (void *)cmd;
hcmd.len[0] = cmd_size;

mutex_lock(&mvm->mutex);
ret = iwl_mvm_send_cmd(mvm, &hcmd);
mutex_unlock(&mvm->mutex);

kfree(cmd);

if (ret < 0)
return ret;

rsp = (void *)hcmd.resp_pkt->data;
if (rsp->status != DEBUG_MEM_STATUS_SUCCESS) {
ret = -ENXIO;
goto out;
}

ret = data_size;
*ppos += ret;

out:
iwl_free_resp(&hcmd);
return ret;
}

static const struct file_operations iwl_dbgfs_mem_ops = {
.read = iwl_dbgfs_mem_read,
.write = iwl_dbgfs_mem_write,
.open = simple_open,
.llseek = default_llseek,
};

int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
{
struct dentry *bcast_dir __maybe_unused;
Expand Down Expand Up @@ -1615,6 +1741,9 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
mvm->debugfs_dir, &mvm->nvm_phy_sku_blob))
goto err;

debugfs_create_file("mem", S_IRUSR | S_IWUSR, dbgfs_dir, mvm,
&iwl_dbgfs_mem_ops);

/*
* Create a symlink with mac80211. It will be removed when mac80211
* exists (before the opmode exists which removes the target.)
Expand Down
50 changes: 50 additions & 0 deletions drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,11 @@ enum iwl_prot_offload_subcmd_ids {
STORED_BEACON_NTF = 0xFF,
};

enum iwl_fmac_debug_cmds {
LMAC_RD_WR = 0x0,
UMAC_RD_WR = 0x1,
};

/* command groups */
enum {
LEGACY_GROUP = 0x0,
Expand All @@ -349,6 +354,7 @@ enum {
PHY_OPS_GROUP = 0x4,
DATA_PATH_GROUP = 0x5,
PROT_OFFLOAD_GROUP = 0xb,
DEBUG_GROUP = 0xf,
};

/**
Expand Down Expand Up @@ -2149,4 +2155,48 @@ struct iwl_channel_switch_noa_notif {
__le32 id_and_color;
} __packed; /* CHANNEL_SWITCH_START_NTFY_API_S_VER_1 */

/* Operation types for the debug mem access */
enum {
DEBUG_MEM_OP_READ = 0,
DEBUG_MEM_OP_WRITE = 1,
DEBUG_MEM_OP_WRITE_BYTES = 2,
};

#define DEBUG_MEM_MAX_SIZE_DWORDS 32

/**
* struct iwl_dbg_mem_access_cmd - Request the device to read/write memory
* @op: DEBUG_MEM_OP_*
* @addr: address to read/write from/to
* @len: in dwords, to read/write
* @data: for write opeations, contains the source buffer
*/
struct iwl_dbg_mem_access_cmd {
__le32 op;
__le32 addr;
__le32 len;
__le32 data[];
} __packed; /* DEBUG_(U|L)MAC_RD_WR_CMD_API_S_VER_1 */

/* Status responses for the debug mem access */
enum {
DEBUG_MEM_STATUS_SUCCESS = 0x0,
DEBUG_MEM_STATUS_FAILED = 0x1,
DEBUG_MEM_STATUS_LOCKED = 0x2,
DEBUG_MEM_STATUS_HIDDEN = 0x3,
DEBUG_MEM_STATUS_LENGTH = 0x4,
};

/**
* struct iwl_dbg_mem_access_rsp - Response to debug mem commands
* @status: DEBUG_MEM_STATUS_*
* @len: read dwords (0 for write operations)
* @data: contains the read DWs
*/
struct iwl_dbg_mem_access_rsp {
__le32 status;
__le32 len;
__le32 data[];
} __packed; /* DEBUG_(U|L)MAC_RD_WR_RSP_API_S_VER_1 */

#endif /* __fw_api_h__ */

0 comments on commit 2b55f43

Please sign in to comment.