int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, struct iwl_mvm_dump_desc *desc, struct iwl_fw_dbg_trigger_tlv *trigger) { unsigned int delay = 0; if (trigger) delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay)); if (test_and_set_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status)) return -EBUSY; if (WARN_ON(mvm->fw_dump_desc)) iwl_mvm_free_fw_dump_desc(mvm); IWL_WARN(mvm, "Collecting data: trigger %d fired.\n", le32_to_cpu(desc->trig_desc.type)); mvm->fw_dump_desc = desc; mvm->fw_dump_trig = trigger; queue_delayed_work(system_wq, &mvm->fw_dump_wk, delay); return 0; }
void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) { struct iwl_fw_error_dump_file *dump_file; struct iwl_fw_error_dump_data *dump_data; struct iwl_fw_error_dump_info *dump_info; struct iwl_fw_error_dump_mem *dump_mem; struct iwl_fw_error_dump_trigger_desc *dump_trig; struct iwl_mvm_dump_ptrs *fw_error_dump; u32 sram_len, sram_ofs; struct iwl_fw_dbg_mem_seg_tlv * const *fw_dbg_mem = mvm->fw->dbg_mem_tlv; u32 file_len, fifo_data_len = 0, prph_len = 0, radio_len = 0; u32 smem_len = mvm->fw->dbg_dynamic_mem ? 0 : mvm->cfg->smem_len; u32 sram2_len = mvm->fw->dbg_dynamic_mem ? 0 : mvm->cfg->dccm2_len; bool monitor_dump_only = false; int i; if (!IWL_MVM_COLLECT_FW_ERR_DUMP && !mvm->trans->dbg_dest_tlv) return; lockdep_assert_held(&mvm->mutex); /* there's no point in fw dump if the bus is dead */ if (test_bit(STATUS_TRANS_DEAD, &mvm->trans->status)) { IWL_ERR(mvm, "Skip fw error dump since bus is dead\n"); goto out; } if (mvm->fw_dump_trig && mvm->fw_dump_trig->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY) monitor_dump_only = true; fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL); if (!fw_error_dump) goto out; /* SRAM - include stack CCM if driver knows the values for it */ if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) { const struct fw_img *img; img = &mvm->fw->img[mvm->cur_ucode]; sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; } else { sram_ofs = mvm->cfg->dccm_offset; sram_len = mvm->cfg->dccm_len; } /* reading RXF/TXF sizes */ if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) { struct iwl_mvm_shared_mem_cfg *mem_cfg = &mvm->shared_mem_cfg; fifo_data_len = 0; /* Count RXF size */ for (i = 0; i < ARRAY_SIZE(mem_cfg->rxfifo_size); i++) { if (!mem_cfg->rxfifo_size[i]) continue; /* Add header info */ fifo_data_len += mem_cfg->rxfifo_size[i] + sizeof(*dump_data) + sizeof(struct iwl_fw_error_dump_fifo); } for (i = 0; i < ARRAY_SIZE(mem_cfg->txfifo_size); i++) { if (!mem_cfg->txfifo_size[i]) continue; /* Add header info */ fifo_data_len += mem_cfg->txfifo_size[i] + sizeof(*dump_data) + sizeof(struct iwl_fw_error_dump_fifo); } if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) { for (i = 0; i < ARRAY_SIZE(mem_cfg->internal_txfifo_size); i++) { if (!mem_cfg->internal_txfifo_size[i]) continue; /* Add header info */ fifo_data_len += mem_cfg->internal_txfifo_size[i] + sizeof(*dump_data) + sizeof(struct iwl_fw_error_dump_fifo); } } /* Make room for PRPH registers */ for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) { /* The range includes both boundaries */ int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - iwl_prph_dump_addr[i].start + 4; prph_len += sizeof(*dump_data) + sizeof(struct iwl_fw_error_dump_prph) + num_bytes_in_chunk; } if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) radio_len = sizeof(*dump_data) + RADIO_REG_MAX_READ; } file_len = sizeof(*dump_file) + sizeof(*dump_data) * 2 + fifo_data_len + prph_len + radio_len + sizeof(*dump_info); /* Make room for the SMEM, if it exists */ if (smem_len) file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len; /* Make room for the secondary SRAM, if it exists */ if (sram2_len) file_len += sizeof(*dump_data) + sizeof(*dump_mem) + sram2_len; /* Make room for MEM segments */ for (i = 0; i < ARRAY_SIZE(mvm->fw->dbg_mem_tlv); i++) { if (fw_dbg_mem[i]) file_len += sizeof(*dump_data) + sizeof(*dump_mem) + le32_to_cpu(fw_dbg_mem[i]->len); } /* Make room for fw's virtual image pages, if it exists */ if (mvm->fw->img[mvm->cur_ucode].paging_mem_size && mvm->fw_paging_db[0].fw_paging_block) file_len += mvm->num_of_paging_blk * (sizeof(*dump_data) + sizeof(struct iwl_fw_error_dump_paging) + PAGING_BLOCK_SIZE); /* If we only want a monitor dump, reset the file length */ if (monitor_dump_only) { file_len = sizeof(*dump_file) + sizeof(*dump_data) + sizeof(*dump_info); } /* * In 8000 HW family B-step include the ICCM (which resides separately) */ if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 && CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) file_len += sizeof(*dump_data) + sizeof(*dump_mem) + IWL8260_ICCM_LEN; if (mvm->fw_dump_desc) file_len += sizeof(*dump_data) + sizeof(*dump_trig) + mvm->fw_dump_desc->len; if (!mvm->fw->dbg_dynamic_mem) file_len += sram_len + sizeof(*dump_mem); dump_file = vzalloc(file_len); if (!dump_file) { kfree(fw_error_dump); goto out; } fw_error_dump->op_mode_ptr = dump_file; dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); dump_data = (void *)dump_file->data; dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO); dump_data->len = cpu_to_le32(sizeof(*dump_info)); dump_info = (void *)dump_data->data; dump_info->device_family = mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ? cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) : cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8); dump_info->hw_step = cpu_to_le32(CSR_HW_REV_STEP(mvm->trans->hw_rev)); memcpy(dump_info->fw_human_readable, mvm->fw->human_readable, sizeof(dump_info->fw_human_readable)); strncpy(dump_info->dev_human_readable, mvm->cfg->name, sizeof(dump_info->dev_human_readable)); strncpy(dump_info->bus_human_readable, mvm->dev->bus->name, sizeof(dump_info->bus_human_readable)); dump_data = iwl_fw_error_next_data(dump_data); /* We only dump the FIFOs if the FW is in error state */ if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) { iwl_mvm_dump_fifos(mvm, &dump_data); if (radio_len) iwl_mvm_read_radio_reg(mvm, &dump_data); } if (mvm->fw_dump_desc) { dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO); dump_data->len = cpu_to_le32(sizeof(*dump_trig) + mvm->fw_dump_desc->len); dump_trig = (void *)dump_data->data; memcpy(dump_trig, &mvm->fw_dump_desc->trig_desc, sizeof(*dump_trig) + mvm->fw_dump_desc->len); dump_data = iwl_fw_error_next_data(dump_data); } /* In case we only want monitor dump, skip to dump trasport data */ if (monitor_dump_only) goto dump_trans_data; if (!mvm->fw->dbg_dynamic_mem) { dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem)); dump_mem = (void *)dump_data->data; dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); dump_mem->offset = cpu_to_le32(sram_ofs); iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_mem->data, sram_len); dump_data = iwl_fw_error_next_data(dump_data); } for (i = 0; i < ARRAY_SIZE(mvm->fw->dbg_mem_tlv); i++) { if (fw_dbg_mem[i]) { u32 len = le32_to_cpu(fw_dbg_mem[i]->len); u32 ofs = le32_to_cpu(fw_dbg_mem[i]->ofs); dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); dump_data->len = cpu_to_le32(len + sizeof(*dump_mem)); dump_mem = (void *)dump_data->data; dump_mem->type = fw_dbg_mem[i]->data_type; dump_mem->offset = cpu_to_le32(ofs); iwl_trans_read_mem_bytes(mvm->trans, ofs, dump_mem->data, len); dump_data = iwl_fw_error_next_data(dump_data); } } if (smem_len) { dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem)); dump_mem = (void *)dump_data->data; dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM); dump_mem->offset = cpu_to_le32(mvm->cfg->smem_offset); iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->smem_offset, dump_mem->data, smem_len); dump_data = iwl_fw_error_next_data(dump_data); } if (sram2_len) { dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); dump_data->len = cpu_to_le32(sram2_len + sizeof(*dump_mem)); dump_mem = (void *)dump_data->data; dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); dump_mem->offset = cpu_to_le32(mvm->cfg->dccm2_offset); iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->dccm2_offset, dump_mem->data, sram2_len); dump_data = iwl_fw_error_next_data(dump_data); } if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 && CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) { dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); dump_data->len = cpu_to_le32(IWL8260_ICCM_LEN + sizeof(*dump_mem)); dump_mem = (void *)dump_data->data; dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); dump_mem->offset = cpu_to_le32(IWL8260_ICCM_OFFSET); iwl_trans_read_mem_bytes(mvm->trans, IWL8260_ICCM_OFFSET, dump_mem->data, IWL8260_ICCM_LEN); dump_data = iwl_fw_error_next_data(dump_data); } /* Dump fw's virtual image */ if (mvm->fw->img[mvm->cur_ucode].paging_mem_size && mvm->fw_paging_db[0].fw_paging_block) { for (i = 1; i < mvm->num_of_paging_blk + 1; i++) { struct iwl_fw_error_dump_paging *paging; struct page *pages = mvm->fw_paging_db[i].fw_paging_block; dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING); dump_data->len = cpu_to_le32(sizeof(*paging) + PAGING_BLOCK_SIZE); paging = (void *)dump_data->data; paging->index = cpu_to_le32(i); memcpy(paging->data, page_address(pages), PAGING_BLOCK_SIZE); dump_data = iwl_fw_error_next_data(dump_data); } } if (prph_len) iwl_dump_prph(mvm->trans, &dump_data); dump_trans_data: fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans, mvm->fw_dump_trig); fw_error_dump->op_mode_len = file_len; if (fw_error_dump->trans_ptr) file_len += fw_error_dump->trans_ptr->len; dump_file->file_len = cpu_to_le32(file_len); dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0, GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump); out: iwl_mvm_free_fw_dump_desc(mvm); mvm->fw_dump_trig = NULL; clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status); }