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>
This commit is contained in:
parent
186cd49a4d
commit
2b55f43f8e
|
@ -1518,6 +1518,132 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256);
|
||||||
MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8);
|
MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8);
|
||||||
#endif
|
#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)
|
int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
|
||||||
{
|
{
|
||||||
struct dentry *bcast_dir __maybe_unused;
|
struct dentry *bcast_dir __maybe_unused;
|
||||||
|
@ -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))
|
mvm->debugfs_dir, &mvm->nvm_phy_sku_blob))
|
||||||
goto err;
|
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
|
* Create a symlink with mac80211. It will be removed when mac80211
|
||||||
* exists (before the opmode exists which removes the target.)
|
* exists (before the opmode exists which removes the target.)
|
||||||
|
|
|
@ -340,6 +340,11 @@ enum iwl_prot_offload_subcmd_ids {
|
||||||
STORED_BEACON_NTF = 0xFF,
|
STORED_BEACON_NTF = 0xFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum iwl_fmac_debug_cmds {
|
||||||
|
LMAC_RD_WR = 0x0,
|
||||||
|
UMAC_RD_WR = 0x1,
|
||||||
|
};
|
||||||
|
|
||||||
/* command groups */
|
/* command groups */
|
||||||
enum {
|
enum {
|
||||||
LEGACY_GROUP = 0x0,
|
LEGACY_GROUP = 0x0,
|
||||||
|
@ -349,6 +354,7 @@ enum {
|
||||||
PHY_OPS_GROUP = 0x4,
|
PHY_OPS_GROUP = 0x4,
|
||||||
DATA_PATH_GROUP = 0x5,
|
DATA_PATH_GROUP = 0x5,
|
||||||
PROT_OFFLOAD_GROUP = 0xb,
|
PROT_OFFLOAD_GROUP = 0xb,
|
||||||
|
DEBUG_GROUP = 0xf,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2149,4 +2155,48 @@ struct iwl_channel_switch_noa_notif {
|
||||||
__le32 id_and_color;
|
__le32 id_and_color;
|
||||||
} __packed; /* CHANNEL_SWITCH_START_NTFY_API_S_VER_1 */
|
} __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__ */
|
#endif /* __fw_api_h__ */
|
||||||
|
|
Loading…
Reference in New Issue