diff options
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/fw/dbg.c')
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 331 |
1 files changed, 275 insertions, 56 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 7ad9cee925da..abf49022edbe 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -12,7 +12,7 @@ #include "iwl-io.h" #include "iwl-prph.h" #include "iwl-csr.h" - +#include "iwl-fh.h" /** * struct iwl_fw_dump_ptrs - set of pointers needed for the fw-error-dump * @@ -303,9 +303,6 @@ static void iwl_fw_dump_txf(struct iwl_fw_runtime *fwrt, iwl_trans_release_nic_access(fwrt->trans); } -#define IWL8260_ICCM_OFFSET 0x44000 /* Only for B-step */ -#define IWL8260_ICCM_LEN 0xC000 /* Only for B-step */ - struct iwl_prph_range { u32 start, end; }; @@ -1027,7 +1024,7 @@ struct iwl_dump_ini_region_data { static int iwl_dump_ini_prph_mac_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_error_dump_range *range = range_ptr; @@ -1052,7 +1049,7 @@ iwl_dump_ini_prph_mac_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_prph_phy_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_error_dump_range *range = range_ptr; @@ -1102,7 +1099,7 @@ iwl_dump_ini_prph_phy_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_csr_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_error_dump_range *range = range_ptr; @@ -1121,7 +1118,7 @@ static int iwl_dump_ini_csr_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_config_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_trans *trans = fwrt->trans; struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; @@ -1153,7 +1150,7 @@ static int iwl_dump_ini_config_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_dev_mem_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_error_dump_range *range = range_ptr; @@ -1175,7 +1172,7 @@ static int iwl_dump_ini_dev_mem_iter(struct iwl_fw_runtime *fwrt, } static int _iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct page *page = fwrt->fw_paging_db[idx].fw_paging_block; struct iwl_fw_ini_error_dump_range *range = range_ptr; @@ -1195,7 +1192,7 @@ static int _iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_error_dump_range *range; u32 page_size; @@ -1204,7 +1201,7 @@ static int iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt, idx++; if (!fwrt->trans->trans_cfg->gen2) - return _iwl_dump_ini_paging_iter(fwrt, range_ptr, idx); + return _iwl_dump_ini_paging_iter(fwrt, range_ptr, range_len, idx); range = range_ptr; page_size = fwrt->trans->init_dram.paging[idx].size; @@ -1220,7 +1217,7 @@ static int iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_mon_dram_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_error_dump_range *range = range_ptr; @@ -1239,7 +1236,7 @@ iwl_dump_ini_mon_dram_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_mon_smem_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_error_dump_range *range = range_ptr; @@ -1307,7 +1304,7 @@ static bool iwl_ini_txf_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_txf_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_error_dump_range *range = range_ptr; @@ -1442,7 +1439,7 @@ static void iwl_ini_get_rxf_data(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_rxf_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_error_dump_range *range = range_ptr; @@ -1509,7 +1506,7 @@ out: static int iwl_dump_ini_err_table_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_region_err_table *err_table = ®->err_table; @@ -1528,7 +1525,7 @@ iwl_dump_ini_err_table_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_special_mem_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_region_special_device_memory *special_mem = @@ -1549,7 +1546,7 @@ iwl_dump_ini_special_mem_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_dbgi_sram_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_error_dump_range *range = range_ptr; @@ -1561,8 +1558,6 @@ iwl_dump_ini_dbgi_sram_iter(struct iwl_fw_runtime *fwrt, return -EBUSY; range->range_data_size = reg->dev_addr.size; - iwl_write_prph_no_grab(fwrt->trans, DBGI_SRAM_TARGET_ACCESS_CFG, - DBGI_SRAM_TARGET_ACCESS_CFG_RESET_ADDRESS_MSK); for (i = 0; i < (le32_to_cpu(reg->dev_addr.size) / 4); i++) { prph_data = iwl_read_prph_no_grab(fwrt->trans, (i % 2) ? DBGI_SRAM_TARGET_ACCESS_RDATA_MSB : @@ -1579,7 +1574,7 @@ iwl_dump_ini_dbgi_sram_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_fw_pkt_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_error_dump_range *range = range_ptr; struct iwl_rx_packet *pkt = reg_data->dump_data->fw_pkt; @@ -1598,10 +1593,37 @@ static int iwl_dump_ini_fw_pkt_iter(struct iwl_fw_runtime *fwrt, return sizeof(*range) + le32_to_cpu(range->range_data_size); } +static int iwl_dump_ini_imr_iter(struct iwl_fw_runtime *fwrt, + struct iwl_dump_ini_region_data *reg_data, + void *range_ptr, u32 range_len, int idx) +{ + /* read the IMR memory and DMA it to SRAM */ + struct iwl_fw_ini_error_dump_range *range = range_ptr; + u64 imr_curr_addr = fwrt->trans->dbg.imr_data.imr_curr_addr; + u32 imr_rem_bytes = fwrt->trans->dbg.imr_data.imr2sram_remainbyte; + u32 sram_addr = fwrt->trans->dbg.imr_data.sram_addr; + u32 sram_size = fwrt->trans->dbg.imr_data.sram_size; + u32 size_to_dump = (imr_rem_bytes > sram_size) ? sram_size : imr_rem_bytes; + + range->range_data_size = cpu_to_le32(size_to_dump); + if (iwl_trans_write_imr_mem(fwrt->trans, sram_addr, + imr_curr_addr, size_to_dump)) { + IWL_ERR(fwrt, "WRT_DEBUG: IMR Memory transfer failed\n"); + return -1; + } + + fwrt->trans->dbg.imr_data.imr_curr_addr = imr_curr_addr + size_to_dump; + fwrt->trans->dbg.imr_data.imr2sram_remainbyte -= size_to_dump; + + iwl_trans_read_mem_bytes(fwrt->trans, sram_addr, range->data, + size_to_dump); + return sizeof(*range) + le32_to_cpu(range->range_data_size); +} + static void * iwl_dump_ini_mem_fill_header(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *data) + void *data, u32 data_len) { struct iwl_fw_ini_error_dump *dump = data; @@ -1677,7 +1699,7 @@ iwl_dump_ini_mon_fill_header(struct iwl_fw_runtime *fwrt, static void * iwl_dump_ini_mon_dram_fill_header(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *data) + void *data, u32 data_len) { struct iwl_fw_ini_monitor_dump *mon_dump = (void *)data; @@ -1688,7 +1710,7 @@ iwl_dump_ini_mon_dram_fill_header(struct iwl_fw_runtime *fwrt, static void * iwl_dump_ini_mon_smem_fill_header(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *data) + void *data, u32 data_len) { struct iwl_fw_ini_monitor_dump *mon_dump = (void *)data; @@ -1697,9 +1719,20 @@ iwl_dump_ini_mon_smem_fill_header(struct iwl_fw_runtime *fwrt, } static void * +iwl_dump_ini_mon_dbgi_fill_header(struct iwl_fw_runtime *fwrt, + struct iwl_dump_ini_region_data *reg_data, + void *data, u32 data_len) +{ + struct iwl_fw_ini_monitor_dump *mon_dump = (void *)data; + + return iwl_dump_ini_mon_fill_header(fwrt, reg_data, mon_dump, + &fwrt->trans->cfg->mon_dbgi_regs); +} + +static void * iwl_dump_ini_err_table_fill_header(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *data) + void *data, u32 data_len) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_err_table_dump *dump = data; @@ -1713,7 +1746,7 @@ iwl_dump_ini_err_table_fill_header(struct iwl_fw_runtime *fwrt, static void * iwl_dump_ini_special_mem_fill_header(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *data) + void *data, u32 data_len) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_special_device_memory *dump = data; @@ -1725,6 +1758,18 @@ iwl_dump_ini_special_mem_fill_header(struct iwl_fw_runtime *fwrt, return dump->data; } +static void * +iwl_dump_ini_imr_fill_header(struct iwl_fw_runtime *fwrt, + struct iwl_dump_ini_region_data *reg_data, + void *data, u32 data_len) +{ + struct iwl_fw_ini_error_dump *dump = data; + + dump->header.version = cpu_to_le32(IWL_INI_DUMP_VER); + + return dump->data; +} + static u32 iwl_dump_ini_mem_ranges(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data) { @@ -1784,6 +1829,26 @@ static u32 iwl_dump_ini_single_range(struct iwl_fw_runtime *fwrt, return 1; } +static u32 iwl_dump_ini_imr_ranges(struct iwl_fw_runtime *fwrt, + struct iwl_dump_ini_region_data *reg_data) +{ + /* range is total number of pages need to copied from + *IMR memory to SRAM and later from SRAM to DRAM + */ + u32 imr_enable = fwrt->trans->dbg.imr_data.imr_enable; + u32 imr_size = fwrt->trans->dbg.imr_data.imr_size; + u32 sram_size = fwrt->trans->dbg.imr_data.sram_size; + + if (imr_enable == 0 || imr_size == 0 || sram_size == 0) { + IWL_DEBUG_INFO(fwrt, + "WRT: Invalid imr data enable: %d, imr_size: %d, sram_size: %d\n", + imr_enable, imr_size, sram_size); + return 0; + } + + return((imr_size % sram_size) ? (imr_size / sram_size + 1) : (imr_size / sram_size)); +} + static u32 iwl_dump_ini_mem_get_size(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data) { @@ -1861,6 +1926,20 @@ iwl_dump_ini_mon_smem_get_size(struct iwl_fw_runtime *fwrt, return size; } +static u32 iwl_dump_ini_mon_dbgi_get_size(struct iwl_fw_runtime *fwrt, + struct iwl_dump_ini_region_data *reg_data) +{ + struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; + u32 size = le32_to_cpu(reg->dev_addr.size); + u32 ranges = iwl_dump_ini_mem_ranges(fwrt, reg_data); + + if (!size || !ranges) + return 0; + + return sizeof(struct iwl_fw_ini_monitor_dump) + ranges * + (size + sizeof(struct iwl_fw_ini_error_dump_range)); +} + static u32 iwl_dump_ini_txf_get_size(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data) { @@ -1948,6 +2027,33 @@ iwl_dump_ini_fw_pkt_get_size(struct iwl_fw_runtime *fwrt, return size; } +static u32 +iwl_dump_ini_imr_get_size(struct iwl_fw_runtime *fwrt, + struct iwl_dump_ini_region_data *reg_data) +{ + u32 size = 0; + u32 ranges = 0; + u32 imr_enable = fwrt->trans->dbg.imr_data.imr_enable; + u32 imr_size = fwrt->trans->dbg.imr_data.imr_size; + u32 sram_size = fwrt->trans->dbg.imr_data.sram_size; + + if (imr_enable == 0 || imr_size == 0 || sram_size == 0) { + IWL_DEBUG_INFO(fwrt, + "WRT: Invalid imr data enable: %d, imr_size: %d, sram_size: %d\n", + imr_enable, imr_size, sram_size); + return size; + } + size = imr_size; + ranges = iwl_dump_ini_imr_ranges(fwrt, reg_data); + if (!size && !ranges) { + IWL_ERR(fwrt, "WRT: imr_size :=%d, ranges :=%d\n", size, ranges); + return 0; + } + size += sizeof(struct iwl_fw_ini_error_dump) + + ranges * sizeof(struct iwl_fw_ini_error_dump_range); + return size; +} + /** * struct iwl_dump_ini_mem_ops - ini memory dump operations * @get_num_of_ranges: returns the number of memory ranges in the region. @@ -1964,10 +2070,10 @@ struct iwl_dump_ini_mem_ops { struct iwl_dump_ini_region_data *reg_data); void *(*fill_mem_hdr)(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *data); + void *data, u32 data_len); int (*fill_range)(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range, int idx); + void *range, u32 range_len, int idx); }; /** @@ -1990,24 +2096,53 @@ static u32 iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, struct list_head *list, struct iwl_fw_ini_error_dump_data *tlv; struct iwl_fw_ini_error_dump_header *header; u32 type = reg->type; - u32 id = le32_to_cpu(reg->id); + u32 id = le32_get_bits(reg->id, IWL_FW_INI_REGION_ID_MASK); u32 num_of_ranges, i, size; - void *range; - - /* - * The higher part of the ID from 2 is irrelevant for - * us, so mask it out. - */ - if (le32_to_cpu(reg->hdr.version) >= 2) - id &= IWL_FW_INI_REGION_V2_MASK; + u8 *range; + u32 free_size; + u64 header_size; + u32 dump_policy = IWL_FW_INI_DUMP_VERBOSE; + + IWL_DEBUG_FW(fwrt, "WRT: Collecting region: dump type=%d, id=%d, type=%d\n", + dump_policy, id, type); + + if (le32_to_cpu(reg->hdr.version) >= 2) { + u32 dp = le32_get_bits(reg->id, + IWL_FW_INI_REGION_DUMP_POLICY_MASK); + + if (dump_policy == IWL_FW_INI_DUMP_VERBOSE && + !(dp & IWL_FW_INI_DEBUG_DUMP_POLICY_NO_LIMIT)) { + IWL_DEBUG_FW(fwrt, + "WRT: no dump - type %d and policy mismatch=%d\n", + dump_policy, dp); + return 0; + } else if (dump_policy == IWL_FW_INI_DUMP_MEDIUM && + !(dp & IWL_FW_IWL_DEBUG_DUMP_POLICY_MAX_LIMIT_5MB)) { + IWL_DEBUG_FW(fwrt, + "WRT: no dump - type %d and policy mismatch=%d\n", + dump_policy, dp); + return 0; + } else if (dump_policy == IWL_FW_INI_DUMP_BRIEF && + !(dp & IWL_FW_INI_DEBUG_DUMP_POLICY_MAX_LIMIT_600KB)) { + IWL_DEBUG_FW(fwrt, + "WRT: no dump - type %d and policy mismatch=%d\n", + dump_policy, dp); + return 0; + } + } if (!ops->get_num_of_ranges || !ops->get_size || !ops->fill_mem_hdr || - !ops->fill_range) + !ops->fill_range) { + IWL_DEBUG_FW(fwrt, "WRT: no ops for collecting data\n"); return 0; + } size = ops->get_size(fwrt, reg_data); - if (!size) + + if (size < sizeof(*header)) { + IWL_DEBUG_FW(fwrt, "WRT: size didn't include space for header\n"); return 0; + } entry = vzalloc(sizeof(*entry) + sizeof(*tlv) + size); if (!entry) @@ -2022,9 +2157,6 @@ static u32 iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, struct list_head *list, tlv->reserved = reg->reserved; tlv->len = cpu_to_le32(size); - IWL_DEBUG_FW(fwrt, "WRT: Collecting region: id=%d, type=%d\n", id, - type); - num_of_ranges = ops->get_num_of_ranges(fwrt, reg_data); header = (void *)tlv->data; @@ -2033,7 +2165,8 @@ static u32 iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, struct list_head *list, header->name_len = cpu_to_le32(IWL_FW_INI_MAX_NAME); memcpy(header->name, reg->name, IWL_FW_INI_MAX_NAME); - range = ops->fill_mem_hdr(fwrt, reg_data, header); + free_size = size; + range = ops->fill_mem_hdr(fwrt, reg_data, header, free_size); if (!range) { IWL_ERR(fwrt, "WRT: Failed to fill region header: id=%d, type=%d\n", @@ -2041,8 +2174,21 @@ static u32 iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, struct list_head *list, goto out_err; } + header_size = range - (u8 *)header; + + if (WARN(header_size > free_size, + "header size %llu > free_size %d", + header_size, free_size)) { + IWL_ERR(fwrt, + "WRT: fill_mem_hdr used more than given free_size\n"); + goto out_err; + } + + free_size -= header_size; + for (i = 0; i < num_of_ranges; i++) { - int range_size = ops->fill_range(fwrt, reg_data, range, i); + int range_size = ops->fill_range(fwrt, reg_data, range, + free_size, i); if (range_size < 0) { IWL_ERR(fwrt, @@ -2050,6 +2196,15 @@ static u32 iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, struct list_head *list, id, type); goto out_err; } + + if (WARN(range_size > free_size, "range_size %d > free_size %d", + range_size, free_size)) { + IWL_ERR(fwrt, + "WRT: fill_raged used more than given free_size\n"); + goto out_err; + } + + free_size -= range_size; range = range + range_size; } @@ -2240,7 +2395,12 @@ static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = { .fill_mem_hdr = iwl_dump_ini_mem_fill_header, .fill_range = iwl_dump_ini_csr_iter, }, - [IWL_FW_INI_REGION_DRAM_IMR] = {}, + [IWL_FW_INI_REGION_DRAM_IMR] = { + .get_num_of_ranges = iwl_dump_ini_imr_ranges, + .get_size = iwl_dump_ini_imr_get_size, + .fill_mem_hdr = iwl_dump_ini_imr_fill_header, + .fill_range = iwl_dump_ini_imr_iter, + }, [IWL_FW_INI_REGION_PCI_IOSF_CONFIG] = { .get_num_of_ranges = iwl_dump_ini_mem_ranges, .get_size = iwl_dump_ini_mem_get_size, @@ -2255,8 +2415,8 @@ static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = { }, [IWL_FW_INI_REGION_DBGI_SRAM] = { .get_num_of_ranges = iwl_dump_ini_mem_ranges, - .get_size = iwl_dump_ini_mem_get_size, - .fill_mem_hdr = iwl_dump_ini_mem_fill_header, + .get_size = iwl_dump_ini_mon_dbgi_get_size, + .fill_mem_hdr = iwl_dump_ini_mon_dbgi_fill_header, .fill_range = iwl_dump_ini_dbgi_sram_iter, }, }; @@ -2270,6 +2430,9 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data reg_data = { .dump_data = dump_data, }; + struct iwl_dump_ini_region_data imr_reg_data = { + .dump_data = dump_data, + }; int i; u32 size = 0; u64 regions_mask = le64_to_cpu(trigger->regions_mask) & @@ -2305,10 +2468,32 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, tp_id); continue; } + /* + * DRAM_IMR can be collected only for FW/HW error timepoint + * when fw is not alive. In addition, it must be collected + * lastly as it overwrites SRAM that can possibly contain + * debug data which also need to be collected. + */ + if (reg_type == IWL_FW_INI_REGION_DRAM_IMR) { + if (tp_id == IWL_FW_INI_TIME_POINT_FW_ASSERT || + tp_id == IWL_FW_INI_TIME_POINT_FW_HW_ERROR) + imr_reg_data.reg_tlv = fwrt->trans->dbg.active_regions[i]; + else + IWL_INFO(fwrt, + "WRT: trying to collect DRAM_IMR at time point: %d, skipping\n", + tp_id); + /* continue to next region */ + continue; + } + size += iwl_dump_ini_mem(fwrt, list, ®_data, &iwl_dump_ini_region_ops[reg_type]); } + /* collect DRAM_IMR region in the last */ + if (imr_reg_data.reg_tlv) + size += iwl_dump_ini_mem(fwrt, list, ®_data, + &iwl_dump_ini_region_ops[IWL_FW_INI_REGION_DRAM_IMR]); if (size) size += iwl_dump_ini_info(fwrt, trigger, list); @@ -2444,7 +2629,7 @@ static void iwl_fw_error_dump_data_free(struct iwl_fwrt_dump_data *dump_data) static void iwl_fw_error_ini_dump(struct iwl_fw_runtime *fwrt, struct iwl_fwrt_dump_data *dump_data) { - struct list_head dump_list = LIST_HEAD_INIT(dump_list); + LIST_HEAD(dump_list); struct scatterlist *sg_dump_data; u32 file_len = iwl_dump_ini_file_gen(fwrt, dump_data, &dump_list); @@ -2589,7 +2774,7 @@ int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt, delay = le32_to_cpu(trigger->stop_delay) * USEC_PER_MSEC; } - desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC); + desc = kzalloc(struct_size(desc, trig_desc.data, len), GFP_ATOMIC); if (!desc) return -ENOMEM; @@ -2685,6 +2870,28 @@ int iwl_fw_start_dbg_conf(struct iwl_fw_runtime *fwrt, u8 conf_id) } IWL_EXPORT_SYMBOL(iwl_fw_start_dbg_conf); +void iwl_send_dbg_dump_complete_cmd(struct iwl_fw_runtime *fwrt, + u32 timepoint, + u32 timepoint_data) +{ + struct iwl_dbg_dump_complete_cmd hcmd_data; + struct iwl_host_cmd hcmd = { + .id = WIDE_ID(DEBUG_GROUP, FW_DUMP_COMPLETE_CMD), + .data[0] = &hcmd_data, + .len[0] = sizeof(hcmd_data), + }; + + if (test_bit(STATUS_FW_ERROR, &fwrt->trans->status)) + return; + + if (fw_has_capa(&fwrt->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_DUMP_COMPLETE_SUPPORT)) { + hcmd_data.tp = cpu_to_le32(timepoint); + hcmd_data.tp_data = cpu_to_le32(timepoint_data); + iwl_trans_send_cmd(fwrt->trans, &hcmd); + } +} + /* this function assumes dump_start was called beforehand and dump_end will be * called afterwards */ @@ -2693,10 +2900,16 @@ static void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt, u8 wk_idx) struct iwl_fw_dbg_params params = {0}; struct iwl_fwrt_dump_data *dump_data = &fwrt->dump.wks[wk_idx].dump_data; - + u32 policy; + u32 time_point; if (!test_bit(wk_idx, &fwrt->dump.active_wks)) return; + if (!dump_data->trig) { + IWL_ERR(fwrt, "dump trigger data is not set\n"); + goto out; + } + if (!test_bit(STATUS_DEVICE_ENABLED, &fwrt->trans->status)) { IWL_ERR(fwrt, "Device is not enabled - cannot dump error\n"); goto out; @@ -2719,6 +2932,13 @@ static void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt, u8 wk_idx) iwl_fw_dbg_stop_restart_recording(fwrt, ¶ms, false); + policy = le32_to_cpu(dump_data->trig->apply_policy); + time_point = le32_to_cpu(dump_data->trig->time_point); + + if (policy & IWL_FW_INI_APPLY_POLICY_DUMP_COMPLETE_CMD) { + IWL_DEBUG_FW_INFO(fwrt, "WRT: sending dump complete\n"); + iwl_send_dbg_dump_complete_cmd(fwrt, time_point, 0); + } if (fwrt->trans->dbg.last_tp_resetfw == IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY) iwl_force_nmi(fwrt->trans); @@ -2777,10 +2997,10 @@ int iwl_fw_dbg_ini_collect(struct iwl_fw_runtime *fwrt, "WRT: Collecting data: ini trigger %d fired (delay=%dms).\n", tp_id, (u32)(delay / USEC_PER_MSEC)); - schedule_delayed_work(&fwrt->dump.wks[idx].wk, usecs_to_jiffies(delay)); - if (sync) iwl_fw_dbg_collect_sync(fwrt, idx); + else + schedule_delayed_work(&fwrt->dump.wks[idx].wk, usecs_to_jiffies(delay)); return 0; } @@ -2795,9 +3015,8 @@ void iwl_fw_error_dump_wk(struct work_struct *work) /* assumes the op mode mutex is locked in dump_start since * iwl_fw_dbg_collect_sync can't run in parallel */ - if (fwrt->ops && fwrt->ops->dump_start && - fwrt->ops->dump_start(fwrt->ops_ctx)) - return; + if (fwrt->ops && fwrt->ops->dump_start) + fwrt->ops->dump_start(fwrt->ops_ctx); iwl_fw_dbg_collect_sync(fwrt, wks->idx); |