1405 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause2/*3 * Copyright (C) 2018-2024 Intel Corporation4 */5#include <linux/firmware.h>6#include "iwl-drv.h"7#include "iwl-trans.h"8#include "iwl-dbg-tlv.h"9#include "fw/dbg.h"10#include "fw/runtime.h"11 12/**13 * enum iwl_dbg_tlv_type - debug TLV types14 * @IWL_DBG_TLV_TYPE_DEBUG_INFO: debug info TLV15 * @IWL_DBG_TLV_TYPE_BUF_ALLOC: buffer allocation TLV16 * @IWL_DBG_TLV_TYPE_HCMD: host command TLV17 * @IWL_DBG_TLV_TYPE_REGION: region TLV18 * @IWL_DBG_TLV_TYPE_TRIGGER: trigger TLV19 * @IWL_DBG_TLV_TYPE_CONF_SET: conf set TLV20 * @IWL_DBG_TLV_TYPE_NUM: number of debug TLVs21 */22enum iwl_dbg_tlv_type {23 IWL_DBG_TLV_TYPE_DEBUG_INFO =24 IWL_UCODE_TLV_TYPE_DEBUG_INFO - IWL_UCODE_TLV_DEBUG_BASE,25 IWL_DBG_TLV_TYPE_BUF_ALLOC,26 IWL_DBG_TLV_TYPE_HCMD,27 IWL_DBG_TLV_TYPE_REGION,28 IWL_DBG_TLV_TYPE_TRIGGER,29 IWL_DBG_TLV_TYPE_CONF_SET,30 IWL_DBG_TLV_TYPE_NUM,31};32 33/**34 * struct iwl_dbg_tlv_ver_data - debug TLV version struct35 * @min_ver: min version supported36 * @max_ver: max version supported37 */38struct iwl_dbg_tlv_ver_data {39 int min_ver;40 int max_ver;41};42 43/**44 * struct iwl_dbg_tlv_timer_node - timer node struct45 * @list: list of &struct iwl_dbg_tlv_timer_node46 * @timer: timer47 * @fwrt: &struct iwl_fw_runtime48 * @tlv: TLV attach to the timer node49 */50struct iwl_dbg_tlv_timer_node {51 struct list_head list;52 struct timer_list timer;53 struct iwl_fw_runtime *fwrt;54 struct iwl_ucode_tlv *tlv;55};56 57static const struct iwl_dbg_tlv_ver_data58dbg_ver_table[IWL_DBG_TLV_TYPE_NUM] = {59 [IWL_DBG_TLV_TYPE_DEBUG_INFO] = {.min_ver = 1, .max_ver = 1,},60 [IWL_DBG_TLV_TYPE_BUF_ALLOC] = {.min_ver = 1, .max_ver = 1,},61 [IWL_DBG_TLV_TYPE_HCMD] = {.min_ver = 1, .max_ver = 1,},62 [IWL_DBG_TLV_TYPE_REGION] = {.min_ver = 1, .max_ver = 3,},63 [IWL_DBG_TLV_TYPE_TRIGGER] = {.min_ver = 1, .max_ver = 1,},64 [IWL_DBG_TLV_TYPE_CONF_SET] = {.min_ver = 1, .max_ver = 1,},65};66 67/* add a new TLV node, returning it so it can be modified */68static struct iwl_ucode_tlv *iwl_dbg_tlv_add(const struct iwl_ucode_tlv *tlv,69 struct list_head *list)70{71 u32 len = le32_to_cpu(tlv->length);72 struct iwl_dbg_tlv_node *node;73 74 node = kzalloc(struct_size(node, tlv.data, len), GFP_KERNEL);75 if (!node)76 return NULL;77 78 memcpy(&node->tlv, tlv, sizeof(node->tlv));79 memcpy(node->tlv.data, tlv->data, len);80 list_add_tail(&node->list, list);81 82 return &node->tlv;83}84 85static bool iwl_dbg_tlv_ver_support(const struct iwl_ucode_tlv *tlv)86{87 const struct iwl_fw_ini_header *hdr = (const void *)&tlv->data[0];88 u32 type = le32_to_cpu(tlv->type);89 u32 tlv_idx = type - IWL_UCODE_TLV_DEBUG_BASE;90 u32 ver = le32_to_cpu(hdr->version);91 92 if (ver < dbg_ver_table[tlv_idx].min_ver ||93 ver > dbg_ver_table[tlv_idx].max_ver)94 return false;95 96 return true;97}98 99static int iwl_dbg_tlv_alloc_debug_info(struct iwl_trans *trans,100 const struct iwl_ucode_tlv *tlv)101{102 const struct iwl_fw_ini_debug_info_tlv *debug_info = (const void *)tlv->data;103 104 if (le32_to_cpu(tlv->length) != sizeof(*debug_info))105 return -EINVAL;106 107 /* we use this as a string, ensure input was NUL terminated */108 if (strnlen(debug_info->debug_cfg_name,109 sizeof(debug_info->debug_cfg_name)) ==110 sizeof(debug_info->debug_cfg_name))111 return -EINVAL;112 113 IWL_DEBUG_FW(trans, "WRT: Loading debug cfg: %s\n",114 debug_info->debug_cfg_name);115 116 if (!iwl_dbg_tlv_add(tlv, &trans->dbg.debug_info_tlv_list))117 return -ENOMEM;118 return 0;119}120 121static int iwl_dbg_tlv_alloc_buf_alloc(struct iwl_trans *trans,122 const struct iwl_ucode_tlv *tlv)123{124 const struct iwl_fw_ini_allocation_tlv *alloc = (const void *)tlv->data;125 u32 buf_location;126 u32 alloc_id;127 128 if (le32_to_cpu(tlv->length) != sizeof(*alloc))129 return -EINVAL;130 131 buf_location = le32_to_cpu(alloc->buf_location);132 alloc_id = le32_to_cpu(alloc->alloc_id);133 134 if (buf_location == IWL_FW_INI_LOCATION_INVALID ||135 buf_location >= IWL_FW_INI_LOCATION_NUM)136 goto err;137 138 if (alloc_id == IWL_FW_INI_ALLOCATION_INVALID ||139 alloc_id >= IWL_FW_INI_ALLOCATION_NUM)140 goto err;141 142 if (buf_location == IWL_FW_INI_LOCATION_NPK_PATH &&143 alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1)144 goto err;145 146 if (buf_location == IWL_FW_INI_LOCATION_SRAM_PATH &&147 alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1)148 goto err;149 150 if (buf_location == IWL_FW_INI_LOCATION_DRAM_PATH &&151 alloc->req_size == 0) {152 IWL_ERR(trans, "WRT: Invalid DRAM buffer allocation requested size (0)\n");153 return -EINVAL;154 }155 156 trans->dbg.fw_mon_cfg[alloc_id] = *alloc;157 158 return 0;159err:160 IWL_ERR(trans,161 "WRT: Invalid allocation id %u and/or location id %u for allocation TLV\n",162 alloc_id, buf_location);163 return -EINVAL;164}165 166static int iwl_dbg_tlv_alloc_hcmd(struct iwl_trans *trans,167 const struct iwl_ucode_tlv *tlv)168{169 const struct iwl_fw_ini_hcmd_tlv *hcmd = (const void *)tlv->data;170 u32 tp = le32_to_cpu(hcmd->time_point);171 172 if (le32_to_cpu(tlv->length) <= sizeof(*hcmd))173 return -EINVAL;174 175 /* Host commands can not be sent in early time point since the FW176 * is not ready177 */178 if (tp == IWL_FW_INI_TIME_POINT_INVALID ||179 tp >= IWL_FW_INI_TIME_POINT_NUM ||180 tp == IWL_FW_INI_TIME_POINT_EARLY) {181 IWL_ERR(trans,182 "WRT: Invalid time point %u for host command TLV\n",183 tp);184 return -EINVAL;185 }186 187 if (!iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].hcmd_list))188 return -ENOMEM;189 return 0;190}191 192static int iwl_dbg_tlv_alloc_region(struct iwl_trans *trans,193 const struct iwl_ucode_tlv *tlv)194{195 const struct iwl_fw_ini_region_tlv *reg = (const void *)tlv->data;196 struct iwl_ucode_tlv **active_reg;197 u32 id = le32_to_cpu(reg->id);198 u8 type = reg->type;199 u32 tlv_len = sizeof(*tlv) + le32_to_cpu(tlv->length);200 201 /*202 * The higher part of the ID from version 2 is debug policy.203 * The id will be only lsb 16 bits, so mask it out.204 */205 if (le32_to_cpu(reg->hdr.version) >= 2)206 id &= IWL_FW_INI_REGION_ID_MASK;207 208 if (le32_to_cpu(tlv->length) < sizeof(*reg))209 return -EINVAL;210 211 /* for safe use of a string from FW, limit it to IWL_FW_INI_MAX_NAME */212 IWL_DEBUG_FW(trans, "WRT: parsing region: %.*s\n",213 IWL_FW_INI_MAX_NAME, reg->name);214 215 if (id >= IWL_FW_INI_MAX_REGION_ID) {216 IWL_ERR(trans, "WRT: Invalid region id %u\n", id);217 return -EINVAL;218 }219 220 if (type <= IWL_FW_INI_REGION_INVALID ||221 type >= IWL_FW_INI_REGION_NUM) {222 IWL_ERR(trans, "WRT: Invalid region type %u\n", type);223 return -EINVAL;224 }225 226 if (type == IWL_FW_INI_REGION_INTERNAL_BUFFER) {227 trans->dbg.imr_data.sram_addr =228 le32_to_cpu(reg->internal_buffer.base_addr);229 trans->dbg.imr_data.sram_size =230 le32_to_cpu(reg->internal_buffer.size);231 }232 233 234 active_reg = &trans->dbg.active_regions[id];235 if (*active_reg) {236 IWL_WARN(trans, "WRT: Overriding region id %u\n", id);237 238 kfree(*active_reg);239 }240 241 *active_reg = kmemdup(tlv, tlv_len, GFP_KERNEL);242 if (!*active_reg)243 return -ENOMEM;244 245 IWL_DEBUG_FW(trans, "WRT: Enabling region id %u type %u\n", id, type);246 247 return 0;248}249 250static int iwl_dbg_tlv_alloc_trigger(struct iwl_trans *trans,251 const struct iwl_ucode_tlv *tlv)252{253 const struct iwl_fw_ini_trigger_tlv *trig = (const void *)tlv->data;254 u32 tp = le32_to_cpu(trig->time_point);255 u32 rf = le32_to_cpu(trig->reset_fw);256 struct iwl_ucode_tlv *new_tlv;257 258 if (le32_to_cpu(tlv->length) < sizeof(*trig))259 return -EINVAL;260 261 if (tp <= IWL_FW_INI_TIME_POINT_INVALID ||262 tp >= IWL_FW_INI_TIME_POINT_NUM) {263 IWL_ERR(trans,264 "WRT: Invalid time point %u for trigger TLV\n",265 tp);266 return -EINVAL;267 }268 269 IWL_DEBUG_FW(trans,270 "WRT: time point %u for trigger TLV with reset_fw %u\n",271 tp, rf);272 trans->dbg.last_tp_resetfw = 0xFF;273 274 new_tlv = iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].trig_list);275 if (!new_tlv)276 return -ENOMEM;277 278 if (!le32_to_cpu(trig->occurrences)) {279 struct iwl_fw_ini_trigger_tlv *new_trig = (void *)new_tlv->data;280 281 new_trig->occurrences = cpu_to_le32(-1);282 }283 284 return 0;285}286 287static int iwl_dbg_tlv_config_set(struct iwl_trans *trans,288 const struct iwl_ucode_tlv *tlv)289{290 const struct iwl_fw_ini_conf_set_tlv *conf_set = (const void *)tlv->data;291 u32 tp = le32_to_cpu(conf_set->time_point);292 u32 type = le32_to_cpu(conf_set->set_type);293 294 if (tp <= IWL_FW_INI_TIME_POINT_INVALID ||295 tp >= IWL_FW_INI_TIME_POINT_NUM) {296 IWL_DEBUG_FW(trans,297 "WRT: Invalid time point %u for config set TLV\n", tp);298 return -EINVAL;299 }300 301 if (type <= IWL_FW_INI_CONFIG_SET_TYPE_INVALID ||302 type >= IWL_FW_INI_CONFIG_SET_TYPE_MAX_NUM) {303 IWL_DEBUG_FW(trans,304 "WRT: Invalid config set type %u for config set TLV\n", type);305 return -EINVAL;306 }307 308 if (!iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].config_list))309 return -ENOMEM;310 return 0;311}312 313static int (*dbg_tlv_alloc[])(struct iwl_trans *trans,314 const struct iwl_ucode_tlv *tlv) = {315 [IWL_DBG_TLV_TYPE_DEBUG_INFO] = iwl_dbg_tlv_alloc_debug_info,316 [IWL_DBG_TLV_TYPE_BUF_ALLOC] = iwl_dbg_tlv_alloc_buf_alloc,317 [IWL_DBG_TLV_TYPE_HCMD] = iwl_dbg_tlv_alloc_hcmd,318 [IWL_DBG_TLV_TYPE_REGION] = iwl_dbg_tlv_alloc_region,319 [IWL_DBG_TLV_TYPE_TRIGGER] = iwl_dbg_tlv_alloc_trigger,320 [IWL_DBG_TLV_TYPE_CONF_SET] = iwl_dbg_tlv_config_set,321};322 323void iwl_dbg_tlv_alloc(struct iwl_trans *trans, const struct iwl_ucode_tlv *tlv,324 bool ext)325{326 enum iwl_ini_cfg_state *cfg_state = ext ?327 &trans->dbg.external_ini_cfg : &trans->dbg.internal_ini_cfg;328 const struct iwl_fw_ini_header *hdr = (const void *)&tlv->data[0];329 u32 type;330 u32 tlv_idx;331 u32 domain;332 int ret;333 334 if (le32_to_cpu(tlv->length) < sizeof(*hdr))335 return;336 337 type = le32_to_cpu(tlv->type);338 tlv_idx = type - IWL_UCODE_TLV_DEBUG_BASE;339 domain = le32_to_cpu(hdr->domain);340 341 if (domain != IWL_FW_INI_DOMAIN_ALWAYS_ON &&342 !(domain & trans->dbg.domains_bitmap)) {343 IWL_DEBUG_FW(trans,344 "WRT: Skipping TLV with disabled domain 0x%0x (0x%0x)\n",345 domain, trans->dbg.domains_bitmap);346 return;347 }348 349 if (tlv_idx >= ARRAY_SIZE(dbg_tlv_alloc) || !dbg_tlv_alloc[tlv_idx]) {350 IWL_ERR(trans, "WRT: Unsupported TLV type 0x%x\n", type);351 goto out_err;352 }353 354 if (!iwl_dbg_tlv_ver_support(tlv)) {355 IWL_ERR(trans, "WRT: Unsupported TLV 0x%x version %u\n", type,356 le32_to_cpu(hdr->version));357 goto out_err;358 }359 360 ret = dbg_tlv_alloc[tlv_idx](trans, tlv);361 if (ret) {362 IWL_WARN(trans,363 "WRT: Failed to allocate TLV 0x%x, ret %d, (ext=%d)\n",364 type, ret, ext);365 goto out_err;366 }367 368 if (*cfg_state == IWL_INI_CFG_STATE_NOT_LOADED)369 *cfg_state = IWL_INI_CFG_STATE_LOADED;370 371 return;372 373out_err:374 *cfg_state = IWL_INI_CFG_STATE_CORRUPTED;375}376 377void iwl_dbg_tlv_del_timers(struct iwl_trans *trans)378{379 struct list_head *timer_list = &trans->dbg.periodic_trig_list;380 struct iwl_dbg_tlv_timer_node *node, *tmp;381 382 list_for_each_entry_safe(node, tmp, timer_list, list) {383 timer_shutdown_sync(&node->timer);384 list_del(&node->list);385 kfree(node);386 }387}388IWL_EXPORT_SYMBOL(iwl_dbg_tlv_del_timers);389 390static void iwl_dbg_tlv_fragments_free(struct iwl_trans *trans,391 enum iwl_fw_ini_allocation_id alloc_id)392{393 struct iwl_fw_mon *fw_mon;394 int i;395 396 if (alloc_id <= IWL_FW_INI_ALLOCATION_INVALID ||397 alloc_id >= IWL_FW_INI_ALLOCATION_NUM)398 return;399 400 fw_mon = &trans->dbg.fw_mon_ini[alloc_id];401 402 for (i = 0; i < fw_mon->num_frags; i++) {403 struct iwl_dram_data *frag = &fw_mon->frags[i];404 405 dma_free_coherent(trans->dev, frag->size, frag->block,406 frag->physical);407 408 frag->physical = 0;409 frag->block = NULL;410 frag->size = 0;411 }412 413 kfree(fw_mon->frags);414 fw_mon->frags = NULL;415 fw_mon->num_frags = 0;416}417 418void iwl_dbg_tlv_free(struct iwl_trans *trans)419{420 struct iwl_dbg_tlv_node *tlv_node, *tlv_node_tmp;421 int i;422 423 iwl_dbg_tlv_del_timers(trans);424 425 for (i = 0; i < ARRAY_SIZE(trans->dbg.active_regions); i++) {426 struct iwl_ucode_tlv **active_reg =427 &trans->dbg.active_regions[i];428 429 kfree(*active_reg);430 *active_reg = NULL;431 }432 433 list_for_each_entry_safe(tlv_node, tlv_node_tmp,434 &trans->dbg.debug_info_tlv_list, list) {435 list_del(&tlv_node->list);436 kfree(tlv_node);437 }438 439 for (i = 0; i < ARRAY_SIZE(trans->dbg.time_point); i++) {440 struct iwl_dbg_tlv_time_point_data *tp =441 &trans->dbg.time_point[i];442 443 list_for_each_entry_safe(tlv_node, tlv_node_tmp, &tp->trig_list,444 list) {445 list_del(&tlv_node->list);446 kfree(tlv_node);447 }448 449 list_for_each_entry_safe(tlv_node, tlv_node_tmp, &tp->hcmd_list,450 list) {451 list_del(&tlv_node->list);452 kfree(tlv_node);453 }454 455 list_for_each_entry_safe(tlv_node, tlv_node_tmp,456 &tp->active_trig_list, list) {457 list_del(&tlv_node->list);458 kfree(tlv_node);459 }460 461 list_for_each_entry_safe(tlv_node, tlv_node_tmp,462 &tp->config_list, list) {463 list_del(&tlv_node->list);464 kfree(tlv_node);465 }466 467 }468 469 for (i = 0; i < ARRAY_SIZE(trans->dbg.fw_mon_ini); i++)470 iwl_dbg_tlv_fragments_free(trans, i);471}472 473static int iwl_dbg_tlv_parse_bin(struct iwl_trans *trans, const u8 *data,474 size_t len)475{476 const struct iwl_ucode_tlv *tlv;477 u32 tlv_len;478 479 while (len >= sizeof(*tlv)) {480 len -= sizeof(*tlv);481 tlv = (const void *)data;482 483 tlv_len = le32_to_cpu(tlv->length);484 485 if (len < tlv_len) {486 IWL_ERR(trans, "invalid TLV len: %zd/%u\n",487 len, tlv_len);488 return -EINVAL;489 }490 len -= ALIGN(tlv_len, 4);491 data += sizeof(*tlv) + ALIGN(tlv_len, 4);492 493 iwl_dbg_tlv_alloc(trans, tlv, true);494 }495 496 return 0;497}498 499void iwl_dbg_tlv_load_bin(struct device *dev, struct iwl_trans *trans)500{501 const struct firmware *fw;502 const char *yoyo_bin = "iwl-debug-yoyo.bin";503 int res;504 505 if (!iwlwifi_mod_params.enable_ini ||506 trans->trans_cfg->device_family <= IWL_DEVICE_FAMILY_8000)507 return;508 509 res = firmware_request_nowarn(&fw, yoyo_bin, dev);510 IWL_DEBUG_FW(trans, "%s %s\n", res ? "didn't load" : "loaded", yoyo_bin);511 512 if (res)513 return;514 515 trans->dbg.yoyo_bin_loaded = true;516 517 iwl_dbg_tlv_parse_bin(trans, fw->data, fw->size);518 519 release_firmware(fw);520}521 522void iwl_dbg_tlv_init(struct iwl_trans *trans)523{524 int i;525 526 INIT_LIST_HEAD(&trans->dbg.debug_info_tlv_list);527 INIT_LIST_HEAD(&trans->dbg.periodic_trig_list);528 529 for (i = 0; i < ARRAY_SIZE(trans->dbg.time_point); i++) {530 struct iwl_dbg_tlv_time_point_data *tp =531 &trans->dbg.time_point[i];532 533 INIT_LIST_HEAD(&tp->trig_list);534 INIT_LIST_HEAD(&tp->hcmd_list);535 INIT_LIST_HEAD(&tp->active_trig_list);536 INIT_LIST_HEAD(&tp->config_list);537 }538}539 540static int iwl_dbg_tlv_alloc_fragment(struct iwl_fw_runtime *fwrt,541 struct iwl_dram_data *frag, u32 pages)542{543 void *block = NULL;544 dma_addr_t physical;545 546 if (!frag || frag->size || !pages)547 return -EIO;548 549 /*550 * We try to allocate as many pages as we can, starting with551 * the requested amount and going down until we can allocate552 * something. Because of DIV_ROUND_UP(), pages will never go553 * down to 0 and stop the loop, so stop when pages reaches 1,554 * which is too small anyway.555 */556 while (pages > 1) {557 block = dma_alloc_coherent(fwrt->dev, pages * PAGE_SIZE,558 &physical,559 GFP_KERNEL | __GFP_NOWARN);560 if (block)561 break;562 563 IWL_WARN(fwrt, "WRT: Failed to allocate fragment size %lu\n",564 pages * PAGE_SIZE);565 566 pages = DIV_ROUND_UP(pages, 2);567 }568 569 if (!block)570 return -ENOMEM;571 572 frag->physical = physical;573 frag->block = block;574 frag->size = pages * PAGE_SIZE;575 576 return pages;577}578 579static int iwl_dbg_tlv_alloc_fragments(struct iwl_fw_runtime *fwrt,580 enum iwl_fw_ini_allocation_id alloc_id)581{582 struct iwl_fw_mon *fw_mon;583 struct iwl_fw_ini_allocation_tlv *fw_mon_cfg;584 u32 num_frags, remain_pages, frag_pages;585 int i;586 587 if (alloc_id < IWL_FW_INI_ALLOCATION_INVALID ||588 alloc_id >= IWL_FW_INI_ALLOCATION_NUM)589 return -EIO;590 591 fw_mon_cfg = &fwrt->trans->dbg.fw_mon_cfg[alloc_id];592 fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id];593 594 if (fw_mon->num_frags) {595 for (i = 0; i < fw_mon->num_frags; i++)596 memset(fw_mon->frags[i].block, 0,597 fw_mon->frags[i].size);598 return 0;599 }600 601 if (fw_mon_cfg->buf_location !=602 cpu_to_le32(IWL_FW_INI_LOCATION_DRAM_PATH))603 return 0;604 605 num_frags = le32_to_cpu(fw_mon_cfg->max_frags_num);606 if (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) {607 if (alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1)608 return -EIO;609 num_frags = 1;610 } else if (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ &&611 alloc_id > IWL_FW_INI_ALLOCATION_ID_DBGC3) {612 return -EIO;613 }614 615 remain_pages = DIV_ROUND_UP(le32_to_cpu(fw_mon_cfg->req_size),616 PAGE_SIZE);617 num_frags = min_t(u32, num_frags, BUF_ALLOC_MAX_NUM_FRAGS);618 num_frags = min_t(u32, num_frags, remain_pages);619 frag_pages = DIV_ROUND_UP(remain_pages, num_frags);620 621 fw_mon->frags = kcalloc(num_frags, sizeof(*fw_mon->frags), GFP_KERNEL);622 if (!fw_mon->frags)623 return -ENOMEM;624 625 for (i = 0; i < num_frags; i++) {626 int pages = min_t(u32, frag_pages, remain_pages);627 628 IWL_DEBUG_FW(fwrt,629 "WRT: Allocating DRAM buffer (alloc_id=%u, fragment=%u, size=0x%lx)\n",630 alloc_id, i, pages * PAGE_SIZE);631 632 pages = iwl_dbg_tlv_alloc_fragment(fwrt, &fw_mon->frags[i],633 pages);634 if (pages < 0) {635 u32 alloc_size = le32_to_cpu(fw_mon_cfg->req_size) -636 (remain_pages * PAGE_SIZE);637 638 if (alloc_size < le32_to_cpu(fw_mon_cfg->min_size)) {639 iwl_dbg_tlv_fragments_free(fwrt->trans,640 alloc_id);641 return pages;642 }643 break;644 }645 646 remain_pages -= pages;647 fw_mon->num_frags++;648 }649 650 return 0;651}652 653static int iwl_dbg_tlv_apply_buffer(struct iwl_fw_runtime *fwrt,654 enum iwl_fw_ini_allocation_id alloc_id)655{656 struct iwl_fw_mon *fw_mon;657 u32 remain_frags, num_commands;658 int i, fw_mon_idx = 0;659 660 if (!fw_has_capa(&fwrt->fw->ucode_capa,661 IWL_UCODE_TLV_CAPA_DBG_BUF_ALLOC_CMD_SUPP))662 return 0;663 664 if (alloc_id < IWL_FW_INI_ALLOCATION_INVALID ||665 alloc_id >= IWL_FW_INI_ALLOCATION_NUM)666 return -EIO;667 668 if (le32_to_cpu(fwrt->trans->dbg.fw_mon_cfg[alloc_id].buf_location) !=669 IWL_FW_INI_LOCATION_DRAM_PATH)670 return 0;671 672 fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id];673 674 /* the first fragment of DBGC1 is given to the FW via register675 * or context info676 */677 if (alloc_id == IWL_FW_INI_ALLOCATION_ID_DBGC1)678 fw_mon_idx++;679 680 remain_frags = fw_mon->num_frags - fw_mon_idx;681 if (!remain_frags)682 return 0;683 684 num_commands = DIV_ROUND_UP(remain_frags, BUF_ALLOC_MAX_NUM_FRAGS);685 686 IWL_DEBUG_FW(fwrt, "WRT: Applying DRAM destination (alloc_id=%u)\n",687 alloc_id);688 689 for (i = 0; i < num_commands; i++) {690 u32 num_frags = min_t(u32, remain_frags,691 BUF_ALLOC_MAX_NUM_FRAGS);692 struct iwl_buf_alloc_cmd data = {693 .alloc_id = cpu_to_le32(alloc_id),694 .num_frags = cpu_to_le32(num_frags),695 .buf_location =696 cpu_to_le32(IWL_FW_INI_LOCATION_DRAM_PATH),697 };698 struct iwl_host_cmd hcmd = {699 .id = WIDE_ID(DEBUG_GROUP, BUFFER_ALLOCATION),700 .data[0] = &data,701 .len[0] = sizeof(data),702 .flags = CMD_SEND_IN_RFKILL,703 };704 int ret, j;705 706 for (j = 0; j < num_frags; j++) {707 struct iwl_buf_alloc_frag *frag = &data.frags[j];708 struct iwl_dram_data *fw_mon_frag =709 &fw_mon->frags[fw_mon_idx++];710 711 frag->addr = cpu_to_le64(fw_mon_frag->physical);712 frag->size = cpu_to_le32(fw_mon_frag->size);713 }714 ret = iwl_trans_send_cmd(fwrt->trans, &hcmd);715 if (ret)716 return ret;717 718 remain_frags -= num_frags;719 }720 721 return 0;722}723 724static void iwl_dbg_tlv_apply_buffers(struct iwl_fw_runtime *fwrt)725{726 int ret, i;727 728 if (fw_has_capa(&fwrt->fw->ucode_capa,729 IWL_UCODE_TLV_CAPA_DRAM_FRAG_SUPPORT))730 return;731 732 for (i = 0; i < IWL_FW_INI_ALLOCATION_NUM; i++) {733 ret = iwl_dbg_tlv_apply_buffer(fwrt, i);734 if (ret)735 IWL_WARN(fwrt,736 "WRT: Failed to apply DRAM buffer for allocation id %d, ret=%d\n",737 i, ret);738 }739}740 741static int iwl_dbg_tlv_update_dram(struct iwl_fw_runtime *fwrt,742 enum iwl_fw_ini_allocation_id alloc_id,743 struct iwl_dram_info *dram_info)744{745 struct iwl_fw_mon *fw_mon;746 u32 remain_frags, num_frags;747 int j, fw_mon_idx = 0;748 struct iwl_buf_alloc_cmd *data;749 750 if (le32_to_cpu(fwrt->trans->dbg.fw_mon_cfg[alloc_id].buf_location) !=751 IWL_FW_INI_LOCATION_DRAM_PATH) {752 IWL_DEBUG_FW(fwrt, "WRT: alloc_id %u location is not in DRAM_PATH\n",753 alloc_id);754 return -1;755 }756 757 fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id];758 759 /* the first fragment of DBGC1 is given to the FW via register760 * or context info761 */762 if (alloc_id == IWL_FW_INI_ALLOCATION_ID_DBGC1)763 fw_mon_idx++;764 765 remain_frags = fw_mon->num_frags - fw_mon_idx;766 if (!remain_frags)767 return -1;768 769 num_frags = min_t(u32, remain_frags, BUF_ALLOC_MAX_NUM_FRAGS);770 data = &dram_info->dram_frags[alloc_id - 1];771 data->alloc_id = cpu_to_le32(alloc_id);772 data->num_frags = cpu_to_le32(num_frags);773 data->buf_location = cpu_to_le32(IWL_FW_INI_LOCATION_DRAM_PATH);774 775 IWL_DEBUG_FW(fwrt, "WRT: DRAM buffer details alloc_id=%u, num_frags=%u\n",776 cpu_to_le32(alloc_id), cpu_to_le32(num_frags));777 778 for (j = 0; j < num_frags; j++) {779 struct iwl_buf_alloc_frag *frag = &data->frags[j];780 struct iwl_dram_data *fw_mon_frag = &fw_mon->frags[fw_mon_idx++];781 782 frag->addr = cpu_to_le64(fw_mon_frag->physical);783 frag->size = cpu_to_le32(fw_mon_frag->size);784 IWL_DEBUG_FW(fwrt, "WRT: DRAM fragment details\n");785 IWL_DEBUG_FW(fwrt, "frag=%u, addr=0x%016llx, size=0x%x)\n",786 j, cpu_to_le64(fw_mon_frag->physical),787 cpu_to_le32(fw_mon_frag->size));788 }789 return 0;790}791 792static void iwl_dbg_tlv_update_drams(struct iwl_fw_runtime *fwrt)793{794 int ret, i;795 bool dram_alloc = false;796 struct iwl_dram_data *frags =797 &fwrt->trans->dbg.fw_mon_ini[IWL_FW_INI_ALLOCATION_ID_DBGC1].frags[0];798 struct iwl_dram_info *dram_info;799 800 if (!frags || !frags->block)801 return;802 803 dram_info = frags->block;804 805 if (!fw_has_capa(&fwrt->fw->ucode_capa,806 IWL_UCODE_TLV_CAPA_DRAM_FRAG_SUPPORT))807 return;808 809 memset(dram_info, 0, sizeof(*dram_info));810 811 for (i = IWL_FW_INI_ALLOCATION_ID_DBGC1;812 i < IWL_FW_INI_ALLOCATION_NUM; i++) {813 if (fwrt->trans->dbg.fw_mon_cfg[i].buf_location ==814 IWL_FW_INI_LOCATION_INVALID)815 continue;816 817 ret = iwl_dbg_tlv_update_dram(fwrt, i, dram_info);818 if (!ret)819 dram_alloc = true;820 else821 IWL_INFO(fwrt,822 "WRT: Failed to set DRAM buffer for alloc id %d, ret=%d\n",823 i, ret);824 }825 826 if (dram_alloc) {827 dram_info->first_word = cpu_to_le32(DRAM_INFO_FIRST_MAGIC_WORD);828 dram_info->second_word = cpu_to_le32(DRAM_INFO_SECOND_MAGIC_WORD);829 }830}831 832static void iwl_dbg_tlv_send_hcmds(struct iwl_fw_runtime *fwrt,833 struct list_head *hcmd_list)834{835 struct iwl_dbg_tlv_node *node;836 837 list_for_each_entry(node, hcmd_list, list) {838 struct iwl_fw_ini_hcmd_tlv *hcmd = (void *)node->tlv.data;839 struct iwl_fw_ini_hcmd *hcmd_data = &hcmd->hcmd;840 u16 hcmd_len = le32_to_cpu(node->tlv.length) - sizeof(*hcmd);841 struct iwl_host_cmd cmd = {842 .id = WIDE_ID(hcmd_data->group, hcmd_data->id),843 .len = { hcmd_len, },844 .data = { hcmd_data->data, },845 };846 847 iwl_trans_send_cmd(fwrt->trans, &cmd);848 }849}850 851static void iwl_dbg_tlv_apply_config(struct iwl_fw_runtime *fwrt,852 struct list_head *conf_list)853{854 struct iwl_dbg_tlv_node *node;855 856 list_for_each_entry(node, conf_list, list) {857 struct iwl_fw_ini_conf_set_tlv *config_list = (void *)node->tlv.data;858 u32 count, address, value;859 u32 len = (le32_to_cpu(node->tlv.length) - sizeof(*config_list)) / 8;860 u32 type = le32_to_cpu(config_list->set_type);861 u32 offset = le32_to_cpu(config_list->addr_offset);862 863 switch (type) {864 case IWL_FW_INI_CONFIG_SET_TYPE_DEVICE_PERIPHERY_MAC: {865 if (!iwl_trans_grab_nic_access(fwrt->trans)) {866 IWL_DEBUG_FW(fwrt, "WRT: failed to get nic access\n");867 IWL_DEBUG_FW(fwrt, "WRT: skipping MAC PERIPHERY config\n");868 continue;869 }870 IWL_DEBUG_FW(fwrt, "WRT: MAC PERIPHERY config len: len %u\n", len);871 for (count = 0; count < len; count++) {872 address = le32_to_cpu(config_list->addr_val[count].address);873 value = le32_to_cpu(config_list->addr_val[count].value);874 iwl_trans_write_prph(fwrt->trans, address + offset, value);875 }876 iwl_trans_release_nic_access(fwrt->trans);877 break;878 }879 case IWL_FW_INI_CONFIG_SET_TYPE_DEVICE_MEMORY: {880 for (count = 0; count < len; count++) {881 address = le32_to_cpu(config_list->addr_val[count].address);882 value = le32_to_cpu(config_list->addr_val[count].value);883 iwl_trans_write_mem32(fwrt->trans, address + offset, value);884 IWL_DEBUG_FW(fwrt, "WRT: DEV_MEM: count %u, add: %u val: %u\n",885 count, address, value);886 }887 break;888 }889 case IWL_FW_INI_CONFIG_SET_TYPE_CSR: {890 for (count = 0; count < len; count++) {891 address = le32_to_cpu(config_list->addr_val[count].address);892 value = le32_to_cpu(config_list->addr_val[count].value);893 iwl_write32(fwrt->trans, address + offset, value);894 IWL_DEBUG_FW(fwrt, "WRT: CSR: count %u, add: %u val: %u\n",895 count, address, value);896 }897 break;898 }899 case IWL_FW_INI_CONFIG_SET_TYPE_DBGC_DRAM_ADDR: {900 struct iwl_dbgc1_info dram_info = {};901 struct iwl_dram_data *frags = &fwrt->trans->dbg.fw_mon_ini[1].frags[0];902 __le64 dram_base_addr;903 __le32 dram_size;904 u64 dram_addr;905 u32 ret;906 907 if (!frags)908 break;909 910 dram_base_addr = cpu_to_le64(frags->physical);911 dram_size = cpu_to_le32(frags->size);912 dram_addr = le64_to_cpu(dram_base_addr);913 914 IWL_DEBUG_FW(fwrt, "WRT: dram_base_addr 0x%016llx, dram_size 0x%x\n",915 dram_base_addr, dram_size);916 IWL_DEBUG_FW(fwrt, "WRT: config_list->addr_offset: %u\n",917 le32_to_cpu(config_list->addr_offset));918 for (count = 0; count < len; count++) {919 address = le32_to_cpu(config_list->addr_val[count].address);920 dram_info.dbgc1_add_lsb =921 cpu_to_le32((dram_addr & 0x00000000FFFFFFFFULL) + 0x400);922 dram_info.dbgc1_add_msb =923 cpu_to_le32((dram_addr & 0xFFFFFFFF00000000ULL) >> 32);924 dram_info.dbgc1_size = cpu_to_le32(le32_to_cpu(dram_size) - 0x400);925 ret = iwl_trans_write_mem(fwrt->trans,926 address + offset, &dram_info, 4);927 if (ret) {928 IWL_ERR(fwrt, "Failed to write dram_info to HW_SMEM\n");929 break;930 }931 }932 break;933 }934 case IWL_FW_INI_CONFIG_SET_TYPE_PERIPH_SCRATCH_HWM: {935 u32 debug_token_config =936 le32_to_cpu(config_list->addr_val[0].value);937 938 IWL_DEBUG_FW(fwrt, "WRT: Setting HWM debug token config: %u\n",939 debug_token_config);940 fwrt->trans->dbg.ucode_preset = debug_token_config;941 break;942 }943 default:944 break;945 }946 }947}948 949static void iwl_dbg_tlv_periodic_trig_handler(struct timer_list *t)950{951 struct iwl_dbg_tlv_timer_node *timer_node =952 from_timer(timer_node, t, timer);953 struct iwl_fwrt_dump_data dump_data = {954 .trig = (void *)timer_node->tlv->data,955 };956 int ret;957 958 ret = iwl_fw_dbg_ini_collect(timer_node->fwrt, &dump_data, false);959 if (!ret || ret == -EBUSY) {960 u32 occur = le32_to_cpu(dump_data.trig->occurrences);961 u32 collect_interval = le32_to_cpu(dump_data.trig->data[0]);962 963 if (!occur)964 return;965 966 mod_timer(t, jiffies + msecs_to_jiffies(collect_interval));967 }968}969 970static void iwl_dbg_tlv_set_periodic_trigs(struct iwl_fw_runtime *fwrt)971{972 struct iwl_dbg_tlv_node *node;973 struct list_head *trig_list =974 &fwrt->trans->dbg.time_point[IWL_FW_INI_TIME_POINT_PERIODIC].active_trig_list;975 976 list_for_each_entry(node, trig_list, list) {977 struct iwl_fw_ini_trigger_tlv *trig = (void *)node->tlv.data;978 struct iwl_dbg_tlv_timer_node *timer_node;979 u32 occur = le32_to_cpu(trig->occurrences), collect_interval;980 u32 min_interval = 100;981 982 if (!occur)983 continue;984 985 /* make sure there is at least one dword of data for the986 * interval value987 */988 if (le32_to_cpu(node->tlv.length) <989 sizeof(*trig) + sizeof(__le32)) {990 IWL_ERR(fwrt,991 "WRT: Invalid periodic trigger data was not given\n");992 continue;993 }994 995 if (le32_to_cpu(trig->data[0]) < min_interval) {996 IWL_WARN(fwrt,997 "WRT: Override min interval from %u to %u msec\n",998 le32_to_cpu(trig->data[0]), min_interval);999 trig->data[0] = cpu_to_le32(min_interval);1000 }1001 1002 collect_interval = le32_to_cpu(trig->data[0]);1003 1004 timer_node = kzalloc(sizeof(*timer_node), GFP_KERNEL);1005 if (!timer_node) {1006 IWL_ERR(fwrt,1007 "WRT: Failed to allocate periodic trigger\n");1008 continue;1009 }1010 1011 timer_node->fwrt = fwrt;1012 timer_node->tlv = &node->tlv;1013 timer_setup(&timer_node->timer,1014 iwl_dbg_tlv_periodic_trig_handler, 0);1015 1016 list_add_tail(&timer_node->list,1017 &fwrt->trans->dbg.periodic_trig_list);1018 1019 IWL_DEBUG_FW(fwrt, "WRT: Enabling periodic trigger\n");1020 1021 mod_timer(&timer_node->timer,1022 jiffies + msecs_to_jiffies(collect_interval));1023 }1024}1025 1026static bool is_trig_data_contained(const struct iwl_ucode_tlv *new,1027 const struct iwl_ucode_tlv *old)1028{1029 const struct iwl_fw_ini_trigger_tlv *new_trig = (const void *)new->data;1030 const struct iwl_fw_ini_trigger_tlv *old_trig = (const void *)old->data;1031 const __le32 *new_data = new_trig->data, *old_data = old_trig->data;1032 u32 new_dwords_num = iwl_tlv_array_len(new, new_trig, data);1033 u32 old_dwords_num = iwl_tlv_array_len(old, old_trig, data);1034 int i, j;1035 1036 for (i = 0; i < new_dwords_num; i++) {1037 bool match = false;1038 1039 for (j = 0; j < old_dwords_num; j++) {1040 if (new_data[i] == old_data[j]) {1041 match = true;1042 break;1043 }1044 }1045 if (!match)1046 return false;1047 }1048 1049 return true;1050}1051 1052static int iwl_dbg_tlv_override_trig_node(struct iwl_fw_runtime *fwrt,1053 struct iwl_ucode_tlv *trig_tlv,1054 struct iwl_dbg_tlv_node *node)1055{1056 struct iwl_ucode_tlv *node_tlv = &node->tlv;1057 struct iwl_fw_ini_trigger_tlv *node_trig = (void *)node_tlv->data;1058 struct iwl_fw_ini_trigger_tlv *trig = (void *)trig_tlv->data;1059 u32 policy = le32_to_cpu(trig->apply_policy);1060 u32 size = le32_to_cpu(trig_tlv->length);1061 u32 trig_data_len = size - sizeof(*trig);1062 u32 offset = 0;1063 1064 if (!(policy & IWL_FW_INI_APPLY_POLICY_OVERRIDE_DATA)) {1065 u32 data_len = le32_to_cpu(node_tlv->length) -1066 sizeof(*node_trig);1067 1068 IWL_DEBUG_FW(fwrt,1069 "WRT: Appending trigger data (time point %u)\n",1070 le32_to_cpu(trig->time_point));1071 1072 offset += data_len;1073 size += data_len;1074 } else {1075 IWL_DEBUG_FW(fwrt,1076 "WRT: Overriding trigger data (time point %u)\n",1077 le32_to_cpu(trig->time_point));1078 }1079 1080 if (size != le32_to_cpu(node_tlv->length)) {1081 struct list_head *prev = node->list.prev;1082 struct iwl_dbg_tlv_node *tmp;1083 1084 list_del(&node->list);1085 1086 tmp = krealloc(node, sizeof(*node) + size, GFP_KERNEL);1087 if (!tmp) {1088 IWL_WARN(fwrt,1089 "WRT: No memory to override trigger (time point %u)\n",1090 le32_to_cpu(trig->time_point));1091 1092 list_add(&node->list, prev);1093 1094 return -ENOMEM;1095 }1096 1097 list_add(&tmp->list, prev);1098 node_tlv = &tmp->tlv;1099 node_trig = (void *)node_tlv->data;1100 }1101 1102 memcpy((u8 *)node_trig->data + offset, trig->data, trig_data_len);1103 node_tlv->length = cpu_to_le32(size);1104 1105 if (policy & IWL_FW_INI_APPLY_POLICY_OVERRIDE_CFG) {1106 IWL_DEBUG_FW(fwrt,1107 "WRT: Overriding trigger configuration (time point %u)\n",1108 le32_to_cpu(trig->time_point));1109 1110 /* the first 11 dwords are configuration related */1111 memcpy(node_trig, trig, sizeof(__le32) * 11);1112 }1113 1114 if (policy & IWL_FW_INI_APPLY_POLICY_OVERRIDE_REGIONS) {1115 IWL_DEBUG_FW(fwrt,1116 "WRT: Overriding trigger regions (time point %u)\n",1117 le32_to_cpu(trig->time_point));1118 1119 node_trig->regions_mask = trig->regions_mask;1120 } else {1121 IWL_DEBUG_FW(fwrt,1122 "WRT: Appending trigger regions (time point %u)\n",1123 le32_to_cpu(trig->time_point));1124 1125 node_trig->regions_mask |= trig->regions_mask;1126 }1127 1128 return 0;1129}1130 1131static int1132iwl_dbg_tlv_add_active_trigger(struct iwl_fw_runtime *fwrt,1133 struct list_head *trig_list,1134 struct iwl_ucode_tlv *trig_tlv)1135{1136 struct iwl_fw_ini_trigger_tlv *trig = (void *)trig_tlv->data;1137 struct iwl_dbg_tlv_node *node, *match = NULL;1138 u32 policy = le32_to_cpu(trig->apply_policy);1139 1140 list_for_each_entry(node, trig_list, list) {1141 if (!(policy & IWL_FW_INI_APPLY_POLICY_MATCH_TIME_POINT))1142 break;1143 1144 if (!(policy & IWL_FW_INI_APPLY_POLICY_MATCH_DATA) ||1145 is_trig_data_contained(trig_tlv, &node->tlv)) {1146 match = node;1147 break;1148 }1149 }1150 1151 if (!match) {1152 IWL_DEBUG_FW(fwrt, "WRT: Enabling trigger (time point %u)\n",1153 le32_to_cpu(trig->time_point));1154 if (!iwl_dbg_tlv_add(trig_tlv, trig_list))1155 return -ENOMEM;1156 return 0;1157 }1158 1159 return iwl_dbg_tlv_override_trig_node(fwrt, trig_tlv, match);1160}1161 1162static void1163iwl_dbg_tlv_gen_active_trig_list(struct iwl_fw_runtime *fwrt,1164 struct iwl_dbg_tlv_time_point_data *tp)1165{1166 struct iwl_dbg_tlv_node *node;1167 struct list_head *trig_list = &tp->trig_list;1168 struct list_head *active_trig_list = &tp->active_trig_list;1169 1170 list_for_each_entry(node, trig_list, list) {1171 struct iwl_ucode_tlv *tlv = &node->tlv;1172 1173 iwl_dbg_tlv_add_active_trigger(fwrt, active_trig_list, tlv);1174 }1175}1176 1177static bool iwl_dbg_tlv_check_fw_pkt(struct iwl_fw_runtime *fwrt,1178 struct iwl_fwrt_dump_data *dump_data,1179 union iwl_dbg_tlv_tp_data *tp_data,1180 u32 trig_data)1181{1182 struct iwl_rx_packet *pkt = tp_data->fw_pkt;1183 struct iwl_cmd_header *wanted_hdr = (void *)&trig_data;1184 1185 if (pkt && (pkt->hdr.cmd == wanted_hdr->cmd &&1186 pkt->hdr.group_id == wanted_hdr->group_id)) {1187 struct iwl_rx_packet *fw_pkt =1188 kmemdup(pkt,1189 sizeof(*pkt) + iwl_rx_packet_payload_len(pkt),1190 GFP_ATOMIC);1191 1192 if (!fw_pkt)1193 return false;1194 1195 dump_data->fw_pkt = fw_pkt;1196 1197 return true;1198 }1199 1200 return false;1201}1202 1203static int1204iwl_dbg_tlv_tp_trigger(struct iwl_fw_runtime *fwrt, bool sync,1205 struct list_head *active_trig_list,1206 union iwl_dbg_tlv_tp_data *tp_data,1207 bool (*data_check)(struct iwl_fw_runtime *fwrt,1208 struct iwl_fwrt_dump_data *dump_data,1209 union iwl_dbg_tlv_tp_data *tp_data,1210 u32 trig_data))1211{1212 struct iwl_dbg_tlv_node *node;1213 1214 list_for_each_entry(node, active_trig_list, list) {1215 struct iwl_fwrt_dump_data dump_data = {1216 .trig = (void *)node->tlv.data,1217 };1218 u32 num_data = iwl_tlv_array_len(&node->tlv, dump_data.trig,1219 data);1220 int ret, i;1221 u32 tp = le32_to_cpu(dump_data.trig->time_point);1222 1223 1224 if (!num_data) {1225 ret = iwl_fw_dbg_ini_collect(fwrt, &dump_data, sync);1226 if (ret)1227 return ret;1228 }1229 1230 for (i = 0; i < num_data; i++) {1231 if (!data_check ||1232 data_check(fwrt, &dump_data, tp_data,1233 le32_to_cpu(dump_data.trig->data[i]))) {1234 ret = iwl_fw_dbg_ini_collect(fwrt, &dump_data, sync);1235 if (ret)1236 return ret;1237 1238 break;1239 }1240 }1241 1242 fwrt->trans->dbg.restart_required = false;1243 1244 if (fwrt->trans->trans_cfg->device_family ==1245 IWL_DEVICE_FAMILY_9000) {1246 fwrt->trans->dbg.restart_required = true;1247 } else if (tp == IWL_FW_INI_TIME_POINT_FW_ASSERT &&1248 fwrt->trans->dbg.last_tp_resetfw ==1249 IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY) {1250 fwrt->trans->dbg.restart_required = false;1251 fwrt->trans->dbg.last_tp_resetfw = 0xFF;1252 } else if (le32_to_cpu(dump_data.trig->reset_fw) ==1253 IWL_FW_INI_RESET_FW_MODE_STOP_AND_RELOAD_FW) {1254 fwrt->trans->dbg.restart_required = true;1255 } else if (le32_to_cpu(dump_data.trig->reset_fw) ==1256 IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY) {1257 fwrt->trans->dbg.restart_required = false;1258 fwrt->trans->dbg.last_tp_resetfw =1259 le32_to_cpu(dump_data.trig->reset_fw);1260 } else if (le32_to_cpu(dump_data.trig->reset_fw) ==1261 IWL_FW_INI_RESET_FW_MODE_NOTHING) {1262 /* nothing */1263 } else {1264 IWL_ERR(fwrt, "WRT: wrong resetfw %d\n",1265 le32_to_cpu(dump_data.trig->reset_fw));1266 }1267 }1268 return 0;1269}1270 1271void iwl_dbg_tlv_init_cfg(struct iwl_fw_runtime *fwrt)1272{1273 enum iwl_fw_ini_buffer_location *ini_dest = &fwrt->trans->dbg.ini_dest;1274 int ret, i;1275 u32 failed_alloc = 0;1276 1277 if (*ini_dest == IWL_FW_INI_LOCATION_INVALID) {1278 IWL_DEBUG_FW(fwrt,1279 "WRT: Generating active triggers list, domain 0x%x\n",1280 fwrt->trans->dbg.domains_bitmap);1281 1282 for (i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.time_point); i++) {1283 struct iwl_dbg_tlv_time_point_data *tp =1284 &fwrt->trans->dbg.time_point[i];1285 1286 iwl_dbg_tlv_gen_active_trig_list(fwrt, tp);1287 }1288 } else if (*ini_dest != IWL_FW_INI_LOCATION_DRAM_PATH) {1289 /* For DRAM, go through the loop below to clear all the buffers1290 * properly on restart, otherwise garbage may be left there and1291 * leak into new debug dumps.1292 */1293 return;1294 }1295 1296 *ini_dest = IWL_FW_INI_LOCATION_INVALID;1297 for (i = 0; i < IWL_FW_INI_ALLOCATION_NUM; i++) {1298 struct iwl_fw_ini_allocation_tlv *fw_mon_cfg =1299 &fwrt->trans->dbg.fw_mon_cfg[i];1300 u32 dest = le32_to_cpu(fw_mon_cfg->buf_location);1301 1302 if (dest == IWL_FW_INI_LOCATION_INVALID) {1303 failed_alloc |= BIT(i);1304 continue;1305 }1306 1307 if (*ini_dest == IWL_FW_INI_LOCATION_INVALID)1308 *ini_dest = dest;1309 1310 if (dest != *ini_dest)1311 continue;1312 1313 ret = iwl_dbg_tlv_alloc_fragments(fwrt, i);1314 1315 if (ret) {1316 IWL_WARN(fwrt,1317 "WRT: Failed to allocate DRAM buffer for allocation id %d, ret=%d\n",1318 i, ret);1319 failed_alloc |= BIT(i);1320 }1321 }1322 1323 if (!failed_alloc)1324 return;1325 1326 for (i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.active_regions) && failed_alloc; i++) {1327 struct iwl_fw_ini_region_tlv *reg;1328 struct iwl_ucode_tlv **active_reg =1329 &fwrt->trans->dbg.active_regions[i];1330 u32 reg_type;1331 1332 if (!*active_reg) {1333 fwrt->trans->dbg.unsupported_region_msk |= BIT(i);1334 continue;1335 }1336 1337 reg = (void *)(*active_reg)->data;1338 reg_type = reg->type;1339 1340 if (reg_type != IWL_FW_INI_REGION_DRAM_BUFFER ||1341 !(BIT(le32_to_cpu(reg->dram_alloc_id)) & failed_alloc))1342 continue;1343 1344 IWL_DEBUG_FW(fwrt,1345 "WRT: removing allocation id %d from region id %d\n",1346 le32_to_cpu(reg->dram_alloc_id), i);1347 1348 failed_alloc &= ~BIT(le32_to_cpu(reg->dram_alloc_id));1349 fwrt->trans->dbg.unsupported_region_msk |= BIT(i);1350 1351 kfree(*active_reg);1352 *active_reg = NULL;1353 }1354}1355 1356void _iwl_dbg_tlv_time_point(struct iwl_fw_runtime *fwrt,1357 enum iwl_fw_ini_time_point tp_id,1358 union iwl_dbg_tlv_tp_data *tp_data,1359 bool sync)1360{1361 struct list_head *hcmd_list, *trig_list, *conf_list;1362 1363 if (!iwl_trans_dbg_ini_valid(fwrt->trans) ||1364 tp_id == IWL_FW_INI_TIME_POINT_INVALID ||1365 tp_id >= IWL_FW_INI_TIME_POINT_NUM)1366 return;1367 1368 hcmd_list = &fwrt->trans->dbg.time_point[tp_id].hcmd_list;1369 trig_list = &fwrt->trans->dbg.time_point[tp_id].active_trig_list;1370 conf_list = &fwrt->trans->dbg.time_point[tp_id].config_list;1371 1372 switch (tp_id) {1373 case IWL_FW_INI_TIME_POINT_EARLY:1374 iwl_dbg_tlv_init_cfg(fwrt);1375 iwl_dbg_tlv_apply_config(fwrt, conf_list);1376 iwl_dbg_tlv_update_drams(fwrt);1377 iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL);1378 break;1379 case IWL_FW_INI_TIME_POINT_AFTER_ALIVE:1380 iwl_dbg_tlv_apply_buffers(fwrt);1381 iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);1382 iwl_dbg_tlv_apply_config(fwrt, conf_list);1383 iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL);1384 break;1385 case IWL_FW_INI_TIME_POINT_PERIODIC:1386 iwl_dbg_tlv_set_periodic_trigs(fwrt);1387 iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);1388 break;1389 case IWL_FW_INI_TIME_POINT_FW_RSP_OR_NOTIF:1390 case IWL_FW_INI_TIME_POINT_MISSED_BEACONS:1391 case IWL_FW_INI_TIME_POINT_FW_DHC_NOTIFICATION:1392 iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);1393 iwl_dbg_tlv_apply_config(fwrt, conf_list);1394 iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data,1395 iwl_dbg_tlv_check_fw_pkt);1396 break;1397 default:1398 iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);1399 iwl_dbg_tlv_apply_config(fwrt, conf_list);1400 iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL);1401 break;1402 }1403}1404IWL_EXPORT_SYMBOL(_iwl_dbg_tlv_time_point);1405