The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/contrib/dev/iwlwifi/iwl-dbg-tlv.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: b2452e5a54c820d1737cb5d80c3b053f


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.