linux/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
   2/*
   3 * Copyright (C) 2018-2021 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_NUM: number of debug TLVs
  20 */
  21enum iwl_dbg_tlv_type {
  22        IWL_DBG_TLV_TYPE_DEBUG_INFO =
  23                IWL_UCODE_TLV_TYPE_DEBUG_INFO - IWL_UCODE_TLV_DEBUG_BASE,
  24        IWL_DBG_TLV_TYPE_BUF_ALLOC,
  25        IWL_DBG_TLV_TYPE_HCMD,
  26        IWL_DBG_TLV_TYPE_REGION,
  27        IWL_DBG_TLV_TYPE_TRIGGER,
  28        IWL_DBG_TLV_TYPE_NUM,
  29};
  30
  31/**
  32 * struct iwl_dbg_tlv_ver_data -  debug TLV version struct
  33 * @min_ver: min version supported
  34 * @max_ver: max version supported
  35 */
  36struct iwl_dbg_tlv_ver_data {
  37        int min_ver;
  38        int max_ver;
  39};
  40
  41/**
  42 * struct iwl_dbg_tlv_timer_node - timer node struct
  43 * @list: list of &struct iwl_dbg_tlv_timer_node
  44 * @timer: timer
  45 * @fwrt: &struct iwl_fw_runtime
  46 * @tlv: TLV attach to the timer node
  47 */
  48struct iwl_dbg_tlv_timer_node {
  49        struct list_head list;
  50        struct timer_list timer;
  51        struct iwl_fw_runtime *fwrt;
  52        struct iwl_ucode_tlv *tlv;
  53};
  54
  55static const struct iwl_dbg_tlv_ver_data
  56dbg_ver_table[IWL_DBG_TLV_TYPE_NUM] = {
  57        [IWL_DBG_TLV_TYPE_DEBUG_INFO]   = {.min_ver = 1, .max_ver = 1,},
  58        [IWL_DBG_TLV_TYPE_BUF_ALLOC]    = {.min_ver = 1, .max_ver = 1,},
  59        [IWL_DBG_TLV_TYPE_HCMD]         = {.min_ver = 1, .max_ver = 1,},
  60        [IWL_DBG_TLV_TYPE_REGION]       = {.min_ver = 1, .max_ver = 1,},
  61        [IWL_DBG_TLV_TYPE_TRIGGER]      = {.min_ver = 1, .max_ver = 1,},
  62};
  63
  64static int iwl_dbg_tlv_add(const struct iwl_ucode_tlv *tlv,
  65                           struct list_head *list)
  66{
  67        u32 len = le32_to_cpu(tlv->length);
  68        struct iwl_dbg_tlv_node *node;
  69
  70        node = kzalloc(sizeof(*node) + len, GFP_KERNEL);
  71        if (!node)
  72                return -ENOMEM;
  73
  74        memcpy(&node->tlv, tlv, sizeof(node->tlv) + len);
  75        list_add_tail(&node->list, list);
  76
  77        return 0;
  78}
  79
  80static bool iwl_dbg_tlv_ver_support(const struct iwl_ucode_tlv *tlv)
  81{
  82        const struct iwl_fw_ini_header *hdr = (const void *)&tlv->data[0];
  83        u32 type = le32_to_cpu(tlv->type);
  84        u32 tlv_idx = type - IWL_UCODE_TLV_DEBUG_BASE;
  85        u32 ver = le32_to_cpu(hdr->version);
  86
  87        if (ver < dbg_ver_table[tlv_idx].min_ver ||
  88            ver > dbg_ver_table[tlv_idx].max_ver)
  89                return false;
  90
  91        return true;
  92}
  93
  94static int iwl_dbg_tlv_alloc_debug_info(struct iwl_trans *trans,
  95                                        const struct iwl_ucode_tlv *tlv)
  96{
  97        const struct iwl_fw_ini_debug_info_tlv *debug_info = (const void *)tlv->data;
  98
  99        if (le32_to_cpu(tlv->length) != sizeof(*debug_info))
 100                return -EINVAL;
 101
 102        IWL_DEBUG_FW(trans, "WRT: Loading debug cfg: %s\n",
 103                     debug_info->debug_cfg_name);
 104
 105        return iwl_dbg_tlv_add(tlv, &trans->dbg.debug_info_tlv_list);
 106}
 107
 108static int iwl_dbg_tlv_alloc_buf_alloc(struct iwl_trans *trans,
 109                                       const struct iwl_ucode_tlv *tlv)
 110{
 111        const struct iwl_fw_ini_allocation_tlv *alloc = (const void *)tlv->data;
 112        u32 buf_location;
 113        u32 alloc_id;
 114
 115        if (le32_to_cpu(tlv->length) != sizeof(*alloc))
 116                return -EINVAL;
 117
 118        buf_location = le32_to_cpu(alloc->buf_location);
 119        alloc_id = le32_to_cpu(alloc->alloc_id);
 120
 121        if (buf_location == IWL_FW_INI_LOCATION_INVALID ||
 122            buf_location >= IWL_FW_INI_LOCATION_NUM)
 123                goto err;
 124
 125        if (alloc_id == IWL_FW_INI_ALLOCATION_INVALID ||
 126            alloc_id >= IWL_FW_INI_ALLOCATION_NUM)
 127                goto err;
 128
 129        if (buf_location == IWL_FW_INI_LOCATION_NPK_PATH &&
 130            alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1)
 131                goto err;
 132
 133        if (buf_location == IWL_FW_INI_LOCATION_SRAM_PATH &&
 134            alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1 &&
 135            alloc_id != IWL_FW_INI_ALLOCATION_ID_INTERNAL)
 136                goto err;
 137
 138        trans->dbg.fw_mon_cfg[alloc_id] = *alloc;
 139
 140        return 0;
 141err:
 142        IWL_ERR(trans,
 143                "WRT: Invalid allocation id %u and/or location id %u for allocation TLV\n",
 144                alloc_id, buf_location);
 145        return -EINVAL;
 146}
 147
 148static int iwl_dbg_tlv_alloc_hcmd(struct iwl_trans *trans,
 149                                  const struct iwl_ucode_tlv *tlv)
 150{
 151        const struct iwl_fw_ini_hcmd_tlv *hcmd = (const void *)tlv->data;
 152        u32 tp = le32_to_cpu(hcmd->time_point);
 153
 154        if (le32_to_cpu(tlv->length) <= sizeof(*hcmd))
 155                return -EINVAL;
 156
 157        /* Host commands can not be sent in early time point since the FW
 158         * is not ready
 159         */
 160        if (tp == IWL_FW_INI_TIME_POINT_INVALID ||
 161            tp >= IWL_FW_INI_TIME_POINT_NUM ||
 162            tp == IWL_FW_INI_TIME_POINT_EARLY) {
 163                IWL_ERR(trans,
 164                        "WRT: Invalid time point %u for host command TLV\n",
 165                        tp);
 166                return -EINVAL;
 167        }
 168
 169        return iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].hcmd_list);
 170}
 171
 172static int iwl_dbg_tlv_alloc_region(struct iwl_trans *trans,
 173                                    const struct iwl_ucode_tlv *tlv)
 174{
 175        const struct iwl_fw_ini_region_tlv *reg = (const void *)tlv->data;
 176        struct iwl_ucode_tlv **active_reg;
 177        u32 id = le32_to_cpu(reg->id);
 178        u32 type = le32_to_cpu(reg->type);
 179        u32 tlv_len = sizeof(*tlv) + le32_to_cpu(tlv->length);
 180
 181        if (le32_to_cpu(tlv->length) < sizeof(*reg))
 182                return -EINVAL;
 183
 184        if (id >= IWL_FW_INI_MAX_REGION_ID) {
 185                IWL_ERR(trans, "WRT: Invalid region id %u\n", id);
 186                return -EINVAL;
 187        }
 188
 189        if (type <= IWL_FW_INI_REGION_INVALID ||
 190            type >= IWL_FW_INI_REGION_NUM) {
 191                IWL_ERR(trans, "WRT: Invalid region type %u\n", type);
 192                return -EINVAL;
 193        }
 194
 195        if (type == IWL_FW_INI_REGION_PCI_IOSF_CONFIG &&
 196            !trans->ops->read_config32) {
 197                IWL_ERR(trans, "WRT: Unsupported region type %u\n", type);
 198                return -EOPNOTSUPP;
 199        }
 200
 201        active_reg = &trans->dbg.active_regions[id];
 202        if (*active_reg) {
 203                IWL_WARN(trans, "WRT: Overriding region id %u\n", id);
 204
 205                kfree(*active_reg);
 206        }
 207
 208        *active_reg = kmemdup(tlv, tlv_len, GFP_KERNEL);
 209        if (!*active_reg)
 210                return -ENOMEM;
 211
 212        IWL_DEBUG_FW(trans, "WRT: Enabling region id %u type %u\n", id, type);
 213
 214        return 0;
 215}
 216
 217static int iwl_dbg_tlv_alloc_trigger(struct iwl_trans *trans,
 218                                     const struct iwl_ucode_tlv *tlv)
 219{
 220        const struct iwl_fw_ini_trigger_tlv *trig = (const void *)tlv->data;
 221        struct iwl_fw_ini_trigger_tlv *dup_trig;
 222        u32 tp = le32_to_cpu(trig->time_point);
 223        struct iwl_ucode_tlv *dup = NULL;
 224        int ret;
 225
 226        if (le32_to_cpu(tlv->length) < sizeof(*trig))
 227                return -EINVAL;
 228
 229        if (tp <= IWL_FW_INI_TIME_POINT_INVALID ||
 230            tp >= IWL_FW_INI_TIME_POINT_NUM) {
 231                IWL_ERR(trans,
 232                        "WRT: Invalid time point %u for trigger TLV\n",
 233                        tp);
 234                return -EINVAL;
 235        }
 236
 237        if (!le32_to_cpu(trig->occurrences)) {
 238                dup = kmemdup(tlv, sizeof(*tlv) + le32_to_cpu(tlv->length),
 239                                GFP_KERNEL);
 240                if (!dup)
 241                        return -ENOMEM;
 242                dup_trig = (void *)dup->data;
 243                dup_trig->occurrences = cpu_to_le32(-1);
 244                tlv = dup;
 245        }
 246
 247        ret = iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].trig_list);
 248        kfree(dup);
 249
 250        return ret;
 251}
 252
 253static int (*dbg_tlv_alloc[])(struct iwl_trans *trans,
 254                              const struct iwl_ucode_tlv *tlv) = {
 255        [IWL_DBG_TLV_TYPE_DEBUG_INFO]   = iwl_dbg_tlv_alloc_debug_info,
 256        [IWL_DBG_TLV_TYPE_BUF_ALLOC]    = iwl_dbg_tlv_alloc_buf_alloc,
 257        [IWL_DBG_TLV_TYPE_HCMD]         = iwl_dbg_tlv_alloc_hcmd,
 258        [IWL_DBG_TLV_TYPE_REGION]       = iwl_dbg_tlv_alloc_region,
 259        [IWL_DBG_TLV_TYPE_TRIGGER]      = iwl_dbg_tlv_alloc_trigger,
 260};
 261
 262void iwl_dbg_tlv_alloc(struct iwl_trans *trans, const struct iwl_ucode_tlv *tlv,
 263                       bool ext)
 264{
 265        const struct iwl_fw_ini_header *hdr = (const void *)&tlv->data[0];
 266        u32 type = le32_to_cpu(tlv->type);
 267        u32 tlv_idx = type - IWL_UCODE_TLV_DEBUG_BASE;
 268        u32 domain = le32_to_cpu(hdr->domain);
 269        enum iwl_ini_cfg_state *cfg_state = ext ?
 270                &trans->dbg.external_ini_cfg : &trans->dbg.internal_ini_cfg;
 271        int ret;
 272
 273        if (domain != IWL_FW_INI_DOMAIN_ALWAYS_ON &&
 274            !(domain & trans->dbg.domains_bitmap)) {
 275                IWL_DEBUG_FW(trans,
 276                             "WRT: Skipping TLV with disabled domain 0x%0x (0x%0x)\n",
 277                             domain, trans->dbg.domains_bitmap);
 278                return;
 279        }
 280
 281        if (tlv_idx >= ARRAY_SIZE(dbg_tlv_alloc) || !dbg_tlv_alloc[tlv_idx]) {
 282                IWL_ERR(trans, "WRT: Unsupported TLV type 0x%x\n", type);
 283                goto out_err;
 284        }
 285
 286        if (!iwl_dbg_tlv_ver_support(tlv)) {
 287                IWL_ERR(trans, "WRT: Unsupported TLV 0x%x version %u\n", type,
 288                        le32_to_cpu(hdr->version));
 289                goto out_err;
 290        }
 291
 292        ret = dbg_tlv_alloc[tlv_idx](trans, tlv);
 293        if (ret) {
 294                IWL_ERR(trans,
 295                        "WRT: Failed to allocate TLV 0x%x, ret %d, (ext=%d)\n",
 296                        type, ret, ext);
 297                goto out_err;
 298        }
 299
 300        if (*cfg_state == IWL_INI_CFG_STATE_NOT_LOADED)
 301                *cfg_state = IWL_INI_CFG_STATE_LOADED;
 302
 303        return;
 304
 305out_err:
 306        *cfg_state = IWL_INI_CFG_STATE_CORRUPTED;
 307}
 308
 309void iwl_dbg_tlv_del_timers(struct iwl_trans *trans)
 310{
 311        struct list_head *timer_list = &trans->dbg.periodic_trig_list;
 312        struct iwl_dbg_tlv_timer_node *node, *tmp;
 313
 314        list_for_each_entry_safe(node, tmp, timer_list, list) {
 315                del_timer(&node->timer);
 316                list_del(&node->list);
 317                kfree(node);
 318        }
 319}
 320IWL_EXPORT_SYMBOL(iwl_dbg_tlv_del_timers);
 321
 322static void iwl_dbg_tlv_fragments_free(struct iwl_trans *trans,
 323                                       enum iwl_fw_ini_allocation_id alloc_id)
 324{
 325        struct iwl_fw_mon *fw_mon;
 326        int i;
 327
 328        if (alloc_id <= IWL_FW_INI_ALLOCATION_INVALID ||
 329            alloc_id >= IWL_FW_INI_ALLOCATION_NUM)
 330                return;
 331
 332        fw_mon = &trans->dbg.fw_mon_ini[alloc_id];
 333
 334        for (i = 0; i < fw_mon->num_frags; i++) {
 335                struct iwl_dram_data *frag = &fw_mon->frags[i];
 336
 337                dma_free_coherent(trans->dev, frag->size, frag->block,
 338                                  frag->physical);
 339
 340                frag->physical = 0;
 341                frag->block = NULL;
 342                frag->size = 0;
 343        }
 344
 345        kfree(fw_mon->frags);
 346        fw_mon->frags = NULL;
 347        fw_mon->num_frags = 0;
 348}
 349
 350void iwl_dbg_tlv_free(struct iwl_trans *trans)
 351{
 352        struct iwl_dbg_tlv_node *tlv_node, *tlv_node_tmp;
 353        int i;
 354
 355        iwl_dbg_tlv_del_timers(trans);
 356
 357        for (i = 0; i < ARRAY_SIZE(trans->dbg.active_regions); i++) {
 358                struct iwl_ucode_tlv **active_reg =
 359                        &trans->dbg.active_regions[i];
 360
 361                kfree(*active_reg);
 362                *active_reg = NULL;
 363        }
 364
 365        list_for_each_entry_safe(tlv_node, tlv_node_tmp,
 366                                 &trans->dbg.debug_info_tlv_list, list) {
 367                list_del(&tlv_node->list);
 368                kfree(tlv_node);
 369        }
 370
 371        for (i = 0; i < ARRAY_SIZE(trans->dbg.time_point); i++) {
 372                struct iwl_dbg_tlv_time_point_data *tp =
 373                        &trans->dbg.time_point[i];
 374
 375                list_for_each_entry_safe(tlv_node, tlv_node_tmp, &tp->trig_list,
 376                                         list) {
 377                        list_del(&tlv_node->list);
 378                        kfree(tlv_node);
 379                }
 380
 381                list_for_each_entry_safe(tlv_node, tlv_node_tmp, &tp->hcmd_list,
 382                                         list) {
 383                        list_del(&tlv_node->list);
 384                        kfree(tlv_node);
 385                }
 386
 387                list_for_each_entry_safe(tlv_node, tlv_node_tmp,
 388                                         &tp->active_trig_list, list) {
 389                        list_del(&tlv_node->list);
 390                        kfree(tlv_node);
 391                }
 392        }
 393
 394        for (i = 0; i < ARRAY_SIZE(trans->dbg.fw_mon_ini); i++)
 395                iwl_dbg_tlv_fragments_free(trans, i);
 396}
 397
 398static int iwl_dbg_tlv_parse_bin(struct iwl_trans *trans, const u8 *data,
 399                                 size_t len)
 400{
 401        const struct iwl_ucode_tlv *tlv;
 402        u32 tlv_len;
 403
 404        while (len >= sizeof(*tlv)) {
 405                len -= sizeof(*tlv);
 406                tlv = (void *)data;
 407
 408                tlv_len = le32_to_cpu(tlv->length);
 409
 410                if (len < tlv_len) {
 411                        IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
 412                                len, tlv_len);
 413                        return -EINVAL;
 414                }
 415                len -= ALIGN(tlv_len, 4);
 416                data += sizeof(*tlv) + ALIGN(tlv_len, 4);
 417
 418                iwl_dbg_tlv_alloc(trans, tlv, true);
 419        }
 420
 421        return 0;
 422}
 423
 424void iwl_dbg_tlv_load_bin(struct device *dev, struct iwl_trans *trans)
 425{
 426        const struct firmware *fw;
 427        int res;
 428
 429        if (!iwlwifi_mod_params.enable_ini ||
 430            trans->trans_cfg->device_family <= IWL_DEVICE_FAMILY_9000)
 431                return;
 432
 433        res = firmware_request_nowarn(&fw, "iwl-debug-yoyo.bin", dev);
 434        if (res)
 435                return;
 436
 437        iwl_dbg_tlv_parse_bin(trans, fw->data, fw->size);
 438
 439        release_firmware(fw);
 440}
 441
 442void iwl_dbg_tlv_init(struct iwl_trans *trans)
 443{
 444        int i;
 445
 446        INIT_LIST_HEAD(&trans->dbg.debug_info_tlv_list);
 447        INIT_LIST_HEAD(&trans->dbg.periodic_trig_list);
 448
 449        for (i = 0; i < ARRAY_SIZE(trans->dbg.time_point); i++) {
 450                struct iwl_dbg_tlv_time_point_data *tp =
 451                        &trans->dbg.time_point[i];
 452
 453                INIT_LIST_HEAD(&tp->trig_list);
 454                INIT_LIST_HEAD(&tp->hcmd_list);
 455                INIT_LIST_HEAD(&tp->active_trig_list);
 456        }
 457}
 458
 459static int iwl_dbg_tlv_alloc_fragment(struct iwl_fw_runtime *fwrt,
 460                                      struct iwl_dram_data *frag, u32 pages)
 461{
 462        void *block = NULL;
 463        dma_addr_t physical;
 464
 465        if (!frag || frag->size || !pages)
 466                return -EIO;
 467
 468        /*
 469         * We try to allocate as many pages as we can, starting with
 470         * the requested amount and going down until we can allocate
 471         * something.  Because of DIV_ROUND_UP(), pages will never go
 472         * down to 0 and stop the loop, so stop when pages reaches 1,
 473         * which is too small anyway.
 474         */
 475        while (pages > 1) {
 476                block = dma_alloc_coherent(fwrt->dev, pages * PAGE_SIZE,
 477                                           &physical,
 478                                           GFP_KERNEL | __GFP_NOWARN);
 479                if (block)
 480                        break;
 481
 482                IWL_WARN(fwrt, "WRT: Failed to allocate fragment size %lu\n",
 483                         pages * PAGE_SIZE);
 484
 485                pages = DIV_ROUND_UP(pages, 2);
 486        }
 487
 488        if (!block)
 489                return -ENOMEM;
 490
 491        frag->physical = physical;
 492        frag->block = block;
 493        frag->size = pages * PAGE_SIZE;
 494
 495        return pages;
 496}
 497
 498static int iwl_dbg_tlv_alloc_fragments(struct iwl_fw_runtime *fwrt,
 499                                       enum iwl_fw_ini_allocation_id alloc_id)
 500{
 501        struct iwl_fw_mon *fw_mon;
 502        struct iwl_fw_ini_allocation_tlv *fw_mon_cfg;
 503        u32 num_frags, remain_pages, frag_pages;
 504        int i;
 505
 506        if (alloc_id < IWL_FW_INI_ALLOCATION_INVALID ||
 507            alloc_id >= IWL_FW_INI_ALLOCATION_NUM)
 508                return -EIO;
 509
 510        fw_mon_cfg = &fwrt->trans->dbg.fw_mon_cfg[alloc_id];
 511        fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id];
 512
 513        if (fw_mon->num_frags ||
 514            fw_mon_cfg->buf_location !=
 515            cpu_to_le32(IWL_FW_INI_LOCATION_DRAM_PATH))
 516                return 0;
 517
 518        num_frags = le32_to_cpu(fw_mon_cfg->max_frags_num);
 519        if (!fw_has_capa(&fwrt->fw->ucode_capa,
 520                         IWL_UCODE_TLV_CAPA_DBG_BUF_ALLOC_CMD_SUPP)) {
 521                if (alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1)
 522                        return -EIO;
 523                num_frags = 1;
 524        }
 525
 526        remain_pages = DIV_ROUND_UP(le32_to_cpu(fw_mon_cfg->req_size),
 527                                    PAGE_SIZE);
 528        num_frags = min_t(u32, num_frags, BUF_ALLOC_MAX_NUM_FRAGS);
 529        num_frags = min_t(u32, num_frags, remain_pages);
 530        frag_pages = DIV_ROUND_UP(remain_pages, num_frags);
 531
 532        fw_mon->frags = kcalloc(num_frags, sizeof(*fw_mon->frags), GFP_KERNEL);
 533        if (!fw_mon->frags)
 534                return -ENOMEM;
 535
 536        for (i = 0; i < num_frags; i++) {
 537                int pages = min_t(u32, frag_pages, remain_pages);
 538
 539                IWL_DEBUG_FW(fwrt,
 540                             "WRT: Allocating DRAM buffer (alloc_id=%u, fragment=%u, size=0x%lx)\n",
 541                             alloc_id, i, pages * PAGE_SIZE);
 542
 543                pages = iwl_dbg_tlv_alloc_fragment(fwrt, &fw_mon->frags[i],
 544                                                   pages);
 545                if (pages < 0) {
 546                        u32 alloc_size = le32_to_cpu(fw_mon_cfg->req_size) -
 547                                (remain_pages * PAGE_SIZE);
 548
 549                        if (alloc_size < le32_to_cpu(fw_mon_cfg->min_size)) {
 550                                iwl_dbg_tlv_fragments_free(fwrt->trans,
 551                                                           alloc_id);
 552                                return pages;
 553                        }
 554                        break;
 555                }
 556
 557                remain_pages -= pages;
 558                fw_mon->num_frags++;
 559        }
 560
 561        return 0;
 562}
 563
 564static int iwl_dbg_tlv_apply_buffer(struct iwl_fw_runtime *fwrt,
 565                                    enum iwl_fw_ini_allocation_id alloc_id)
 566{
 567        struct iwl_fw_mon *fw_mon;
 568        u32 remain_frags, num_commands;
 569        int i, fw_mon_idx = 0;
 570
 571        if (!fw_has_capa(&fwrt->fw->ucode_capa,
 572                         IWL_UCODE_TLV_CAPA_DBG_BUF_ALLOC_CMD_SUPP))
 573                return 0;
 574
 575        if (alloc_id < IWL_FW_INI_ALLOCATION_INVALID ||
 576            alloc_id >= IWL_FW_INI_ALLOCATION_NUM)
 577                return -EIO;
 578
 579        if (le32_to_cpu(fwrt->trans->dbg.fw_mon_cfg[alloc_id].buf_location) !=
 580            IWL_FW_INI_LOCATION_DRAM_PATH)
 581                return 0;
 582
 583        fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id];
 584
 585        /* the first fragment of DBGC1 is given to the FW via register
 586         * or context info
 587         */
 588        if (alloc_id == IWL_FW_INI_ALLOCATION_ID_DBGC1)
 589                fw_mon_idx++;
 590
 591        remain_frags = fw_mon->num_frags - fw_mon_idx;
 592        if (!remain_frags)
 593                return 0;
 594
 595        num_commands = DIV_ROUND_UP(remain_frags, BUF_ALLOC_MAX_NUM_FRAGS);
 596
 597        IWL_DEBUG_FW(fwrt, "WRT: Applying DRAM destination (alloc_id=%u)\n",
 598                     alloc_id);
 599
 600        for (i = 0; i < num_commands; i++) {
 601                u32 num_frags = min_t(u32, remain_frags,
 602                                      BUF_ALLOC_MAX_NUM_FRAGS);
 603                struct iwl_buf_alloc_cmd data = {
 604                        .alloc_id = cpu_to_le32(alloc_id),
 605                        .num_frags = cpu_to_le32(num_frags),
 606                        .buf_location =
 607                                cpu_to_le32(IWL_FW_INI_LOCATION_DRAM_PATH),
 608                };
 609                struct iwl_host_cmd hcmd = {
 610                        .id = WIDE_ID(DEBUG_GROUP, BUFFER_ALLOCATION),
 611                        .data[0] = &data,
 612                        .len[0] = sizeof(data),
 613                };
 614                int ret, j;
 615
 616                for (j = 0; j < num_frags; j++) {
 617                        struct iwl_buf_alloc_frag *frag = &data.frags[j];
 618                        struct iwl_dram_data *fw_mon_frag =
 619                                &fw_mon->frags[fw_mon_idx++];
 620
 621                        frag->addr = cpu_to_le64(fw_mon_frag->physical);
 622                        frag->size = cpu_to_le32(fw_mon_frag->size);
 623                }
 624                ret = iwl_trans_send_cmd(fwrt->trans, &hcmd);
 625                if (ret)
 626                        return ret;
 627
 628                remain_frags -= num_frags;
 629        }
 630
 631        return 0;
 632}
 633
 634static void iwl_dbg_tlv_apply_buffers(struct iwl_fw_runtime *fwrt)
 635{
 636        int ret, i;
 637
 638        for (i = 0; i < IWL_FW_INI_ALLOCATION_NUM; i++) {
 639                ret = iwl_dbg_tlv_apply_buffer(fwrt, i);
 640                if (ret)
 641                        IWL_WARN(fwrt,
 642                                 "WRT: Failed to apply DRAM buffer for allocation id %d, ret=%d\n",
 643                                 i, ret);
 644        }
 645}
 646
 647static void iwl_dbg_tlv_send_hcmds(struct iwl_fw_runtime *fwrt,
 648                                   struct list_head *hcmd_list)
 649{
 650        struct iwl_dbg_tlv_node *node;
 651
 652        list_for_each_entry(node, hcmd_list, list) {
 653                struct iwl_fw_ini_hcmd_tlv *hcmd = (void *)node->tlv.data;
 654                struct iwl_fw_ini_hcmd *hcmd_data = &hcmd->hcmd;
 655                u16 hcmd_len = le32_to_cpu(node->tlv.length) - sizeof(*hcmd);
 656                struct iwl_host_cmd cmd = {
 657                        .id = WIDE_ID(hcmd_data->group, hcmd_data->id),
 658                        .len = { hcmd_len, },
 659                        .data = { hcmd_data->data, },
 660                };
 661
 662                iwl_trans_send_cmd(fwrt->trans, &cmd);
 663        }
 664}
 665
 666static void iwl_dbg_tlv_periodic_trig_handler(struct timer_list *t)
 667{
 668        struct iwl_dbg_tlv_timer_node *timer_node =
 669                from_timer(timer_node, t, timer);
 670        struct iwl_fwrt_dump_data dump_data = {
 671                .trig = (void *)timer_node->tlv->data,
 672        };
 673        int ret;
 674
 675        ret = iwl_fw_dbg_ini_collect(timer_node->fwrt, &dump_data);
 676        if (!ret || ret == -EBUSY) {
 677                u32 occur = le32_to_cpu(dump_data.trig->occurrences);
 678                u32 collect_interval = le32_to_cpu(dump_data.trig->data[0]);
 679
 680                if (!occur)
 681                        return;
 682
 683                mod_timer(t, jiffies + msecs_to_jiffies(collect_interval));
 684        }
 685}
 686
 687static void iwl_dbg_tlv_set_periodic_trigs(struct iwl_fw_runtime *fwrt)
 688{
 689        struct iwl_dbg_tlv_node *node;
 690        struct list_head *trig_list =
 691                &fwrt->trans->dbg.time_point[IWL_FW_INI_TIME_POINT_PERIODIC].active_trig_list;
 692
 693        list_for_each_entry(node, trig_list, list) {
 694                struct iwl_fw_ini_trigger_tlv *trig = (void *)node->tlv.data;
 695                struct iwl_dbg_tlv_timer_node *timer_node;
 696                u32 occur = le32_to_cpu(trig->occurrences), collect_interval;
 697                u32 min_interval = 100;
 698
 699                if (!occur)
 700                        continue;
 701
 702                /* make sure there is at least one dword of data for the
 703                 * interval value
 704                 */
 705                if (le32_to_cpu(node->tlv.length) <
 706                    sizeof(*trig) + sizeof(__le32)) {
 707                        IWL_ERR(fwrt,
 708                                "WRT: Invalid periodic trigger data was not given\n");
 709                        continue;
 710                }
 711
 712                if (le32_to_cpu(trig->data[0]) < min_interval) {
 713                        IWL_WARN(fwrt,
 714                                 "WRT: Override min interval from %u to %u msec\n",
 715                                 le32_to_cpu(trig->data[0]), min_interval);
 716                        trig->data[0] = cpu_to_le32(min_interval);
 717                }
 718
 719                collect_interval = le32_to_cpu(trig->data[0]);
 720
 721                timer_node = kzalloc(sizeof(*timer_node), GFP_KERNEL);
 722                if (!timer_node) {
 723                        IWL_ERR(fwrt,
 724                                "WRT: Failed to allocate periodic trigger\n");
 725                        continue;
 726                }
 727
 728                timer_node->fwrt = fwrt;
 729                timer_node->tlv = &node->tlv;
 730                timer_setup(&timer_node->timer,
 731                            iwl_dbg_tlv_periodic_trig_handler, 0);
 732
 733                list_add_tail(&timer_node->list,
 734                              &fwrt->trans->dbg.periodic_trig_list);
 735
 736                IWL_DEBUG_FW(fwrt, "WRT: Enabling periodic trigger\n");
 737
 738                mod_timer(&timer_node->timer,
 739                          jiffies + msecs_to_jiffies(collect_interval));
 740        }
 741}
 742
 743static bool is_trig_data_contained(const struct iwl_ucode_tlv *new,
 744                                   const struct iwl_ucode_tlv *old)
 745{
 746        const struct iwl_fw_ini_trigger_tlv *new_trig = (const void *)new->data;
 747        const struct iwl_fw_ini_trigger_tlv *old_trig = (const void *)old->data;
 748        const __le32 *new_data = new_trig->data, *old_data = old_trig->data;
 749        u32 new_dwords_num = iwl_tlv_array_len(new, new_trig, data);
 750        u32 old_dwords_num = iwl_tlv_array_len(old, old_trig, data);
 751        int i, j;
 752
 753        for (i = 0; i < new_dwords_num; i++) {
 754                bool match = false;
 755
 756                for (j = 0; j < old_dwords_num; j++) {
 757                        if (new_data[i] == old_data[j]) {
 758                                match = true;
 759                                break;
 760                        }
 761                }
 762                if (!match)
 763                        return false;
 764        }
 765
 766        return true;
 767}
 768
 769static int iwl_dbg_tlv_override_trig_node(struct iwl_fw_runtime *fwrt,
 770                                          struct iwl_ucode_tlv *trig_tlv,
 771                                          struct iwl_dbg_tlv_node *node)
 772{
 773        struct iwl_ucode_tlv *node_tlv = &node->tlv;
 774        struct iwl_fw_ini_trigger_tlv *node_trig = (void *)node_tlv->data;
 775        struct iwl_fw_ini_trigger_tlv *trig = (void *)trig_tlv->data;
 776        u32 policy = le32_to_cpu(trig->apply_policy);
 777        u32 size = le32_to_cpu(trig_tlv->length);
 778        u32 trig_data_len = size - sizeof(*trig);
 779        u32 offset = 0;
 780
 781        if (!(policy & IWL_FW_INI_APPLY_POLICY_OVERRIDE_DATA)) {
 782                u32 data_len = le32_to_cpu(node_tlv->length) -
 783                        sizeof(*node_trig);
 784
 785                IWL_DEBUG_FW(fwrt,
 786                             "WRT: Appending trigger data (time point %u)\n",
 787                             le32_to_cpu(trig->time_point));
 788
 789                offset += data_len;
 790                size += data_len;
 791        } else {
 792                IWL_DEBUG_FW(fwrt,
 793                             "WRT: Overriding trigger data (time point %u)\n",
 794                             le32_to_cpu(trig->time_point));
 795        }
 796
 797        if (size != le32_to_cpu(node_tlv->length)) {
 798                struct list_head *prev = node->list.prev;
 799                struct iwl_dbg_tlv_node *tmp;
 800
 801                list_del(&node->list);
 802
 803                tmp = krealloc(node, sizeof(*node) + size, GFP_KERNEL);
 804                if (!tmp) {
 805                        IWL_WARN(fwrt,
 806                                 "WRT: No memory to override trigger (time point %u)\n",
 807                                 le32_to_cpu(trig->time_point));
 808
 809                        list_add(&node->list, prev);
 810
 811                        return -ENOMEM;
 812                }
 813
 814                list_add(&tmp->list, prev);
 815                node_tlv = &tmp->tlv;
 816                node_trig = (void *)node_tlv->data;
 817        }
 818
 819        memcpy(node_trig->data + offset, trig->data, trig_data_len);
 820        node_tlv->length = cpu_to_le32(size);
 821
 822        if (policy & IWL_FW_INI_APPLY_POLICY_OVERRIDE_CFG) {
 823                IWL_DEBUG_FW(fwrt,
 824                             "WRT: Overriding trigger configuration (time point %u)\n",
 825                             le32_to_cpu(trig->time_point));
 826
 827                /* the first 11 dwords are configuration related */
 828                memcpy(node_trig, trig, sizeof(__le32) * 11);
 829        }
 830
 831        if (policy & IWL_FW_INI_APPLY_POLICY_OVERRIDE_REGIONS) {
 832                IWL_DEBUG_FW(fwrt,
 833                             "WRT: Overriding trigger regions (time point %u)\n",
 834                             le32_to_cpu(trig->time_point));
 835
 836                node_trig->regions_mask = trig->regions_mask;
 837        } else {
 838                IWL_DEBUG_FW(fwrt,
 839                             "WRT: Appending trigger regions (time point %u)\n",
 840                             le32_to_cpu(trig->time_point));
 841
 842                node_trig->regions_mask |= trig->regions_mask;
 843        }
 844
 845        return 0;
 846}
 847
 848static int
 849iwl_dbg_tlv_add_active_trigger(struct iwl_fw_runtime *fwrt,
 850                               struct list_head *trig_list,
 851                               struct iwl_ucode_tlv *trig_tlv)
 852{
 853        struct iwl_fw_ini_trigger_tlv *trig = (void *)trig_tlv->data;
 854        struct iwl_dbg_tlv_node *node, *match = NULL;
 855        u32 policy = le32_to_cpu(trig->apply_policy);
 856
 857        list_for_each_entry(node, trig_list, list) {
 858                if (!(policy & IWL_FW_INI_APPLY_POLICY_MATCH_TIME_POINT))
 859                        break;
 860
 861                if (!(policy & IWL_FW_INI_APPLY_POLICY_MATCH_DATA) ||
 862                    is_trig_data_contained(trig_tlv, &node->tlv)) {
 863                        match = node;
 864                        break;
 865                }
 866        }
 867
 868        if (!match) {
 869                IWL_DEBUG_FW(fwrt, "WRT: Enabling trigger (time point %u)\n",
 870                             le32_to_cpu(trig->time_point));
 871                return iwl_dbg_tlv_add(trig_tlv, trig_list);
 872        }
 873
 874        return iwl_dbg_tlv_override_trig_node(fwrt, trig_tlv, match);
 875}
 876
 877static void
 878iwl_dbg_tlv_gen_active_trig_list(struct iwl_fw_runtime *fwrt,
 879                                 struct iwl_dbg_tlv_time_point_data *tp)
 880{
 881        struct iwl_dbg_tlv_node *node;
 882        struct list_head *trig_list = &tp->trig_list;
 883        struct list_head *active_trig_list = &tp->active_trig_list;
 884
 885        list_for_each_entry(node, trig_list, list) {
 886                struct iwl_ucode_tlv *tlv = &node->tlv;
 887
 888                iwl_dbg_tlv_add_active_trigger(fwrt, active_trig_list, tlv);
 889        }
 890}
 891
 892static bool iwl_dbg_tlv_check_fw_pkt(struct iwl_fw_runtime *fwrt,
 893                                     struct iwl_fwrt_dump_data *dump_data,
 894                                     union iwl_dbg_tlv_tp_data *tp_data,
 895                                     u32 trig_data)
 896{
 897        struct iwl_rx_packet *pkt = tp_data->fw_pkt;
 898        struct iwl_cmd_header *wanted_hdr = (void *)&trig_data;
 899
 900        if (pkt && (pkt->hdr.cmd == wanted_hdr->cmd &&
 901                    pkt->hdr.group_id == wanted_hdr->group_id)) {
 902                struct iwl_rx_packet *fw_pkt =
 903                        kmemdup(pkt,
 904                                sizeof(*pkt) + iwl_rx_packet_payload_len(pkt),
 905                                GFP_ATOMIC);
 906
 907                if (!fw_pkt)
 908                        return false;
 909
 910                dump_data->fw_pkt = fw_pkt;
 911
 912                return true;
 913        }
 914
 915        return false;
 916}
 917
 918static int
 919iwl_dbg_tlv_tp_trigger(struct iwl_fw_runtime *fwrt,
 920                       struct list_head *active_trig_list,
 921                       union iwl_dbg_tlv_tp_data *tp_data,
 922                       bool (*data_check)(struct iwl_fw_runtime *fwrt,
 923                                          struct iwl_fwrt_dump_data *dump_data,
 924                                          union iwl_dbg_tlv_tp_data *tp_data,
 925                                          u32 trig_data))
 926{
 927        struct iwl_dbg_tlv_node *node;
 928
 929        list_for_each_entry(node, active_trig_list, list) {
 930                struct iwl_fwrt_dump_data dump_data = {
 931                        .trig = (void *)node->tlv.data,
 932                };
 933                u32 num_data = iwl_tlv_array_len(&node->tlv, dump_data.trig,
 934                                                 data);
 935                int ret, i;
 936
 937                if (!num_data) {
 938                        ret = iwl_fw_dbg_ini_collect(fwrt, &dump_data);
 939                        if (ret)
 940                                return ret;
 941                }
 942
 943                for (i = 0; i < num_data; i++) {
 944                        if (!data_check ||
 945                            data_check(fwrt, &dump_data, tp_data,
 946                                       le32_to_cpu(dump_data.trig->data[i]))) {
 947                                ret = iwl_fw_dbg_ini_collect(fwrt, &dump_data);
 948                                if (ret)
 949                                        return ret;
 950
 951                                break;
 952                        }
 953                }
 954        }
 955
 956        return 0;
 957}
 958
 959static void iwl_dbg_tlv_init_cfg(struct iwl_fw_runtime *fwrt)
 960{
 961        enum iwl_fw_ini_buffer_location *ini_dest = &fwrt->trans->dbg.ini_dest;
 962        int ret, i;
 963        u32 failed_alloc = 0;
 964
 965        if (*ini_dest != IWL_FW_INI_LOCATION_INVALID)
 966                return;
 967
 968        IWL_DEBUG_FW(fwrt,
 969                     "WRT: Generating active triggers list, domain 0x%x\n",
 970                     fwrt->trans->dbg.domains_bitmap);
 971
 972        for (i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.time_point); i++) {
 973                struct iwl_dbg_tlv_time_point_data *tp =
 974                        &fwrt->trans->dbg.time_point[i];
 975
 976                iwl_dbg_tlv_gen_active_trig_list(fwrt, tp);
 977        }
 978
 979        *ini_dest = IWL_FW_INI_LOCATION_INVALID;
 980        for (i = 0; i < IWL_FW_INI_ALLOCATION_NUM; i++) {
 981                struct iwl_fw_ini_allocation_tlv *fw_mon_cfg =
 982                        &fwrt->trans->dbg.fw_mon_cfg[i];
 983                u32 dest = le32_to_cpu(fw_mon_cfg->buf_location);
 984
 985                if (dest == IWL_FW_INI_LOCATION_INVALID)
 986                        continue;
 987
 988                if (*ini_dest == IWL_FW_INI_LOCATION_INVALID)
 989                        *ini_dest = dest;
 990
 991                if (dest != *ini_dest)
 992                        continue;
 993
 994                ret = iwl_dbg_tlv_alloc_fragments(fwrt, i);
 995
 996                if (ret) {
 997                        IWL_WARN(fwrt,
 998                                 "WRT: Failed to allocate DRAM buffer for allocation id %d, ret=%d\n",
 999                                 i, ret);
1000                        failed_alloc |= BIT(i);
1001                }
1002        }
1003
1004        if (!failed_alloc)
1005                return;
1006
1007        for (i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.active_regions) && failed_alloc; i++) {
1008                struct iwl_fw_ini_region_tlv *reg;
1009                struct iwl_ucode_tlv **active_reg =
1010                        &fwrt->trans->dbg.active_regions[i];
1011                u32 reg_type;
1012
1013                if (!*active_reg)
1014                        continue;
1015
1016                reg = (void *)(*active_reg)->data;
1017                reg_type = le32_to_cpu(reg->type);
1018
1019                if (reg_type != IWL_FW_INI_REGION_DRAM_BUFFER ||
1020                    !(BIT(le32_to_cpu(reg->dram_alloc_id)) & failed_alloc))
1021                        continue;
1022
1023                IWL_DEBUG_FW(fwrt,
1024                             "WRT: removing allocation id %d from region id %d\n",
1025                             le32_to_cpu(reg->dram_alloc_id), i);
1026
1027                failed_alloc &= ~le32_to_cpu(reg->dram_alloc_id);
1028                fwrt->trans->dbg.unsupported_region_msk |= BIT(i);
1029
1030                kfree(*active_reg);
1031                *active_reg = NULL;
1032        }
1033}
1034
1035void iwl_dbg_tlv_time_point(struct iwl_fw_runtime *fwrt,
1036                            enum iwl_fw_ini_time_point tp_id,
1037                            union iwl_dbg_tlv_tp_data *tp_data)
1038{
1039        struct list_head *hcmd_list, *trig_list;
1040
1041        if (!iwl_trans_dbg_ini_valid(fwrt->trans) ||
1042            tp_id == IWL_FW_INI_TIME_POINT_INVALID ||
1043            tp_id >= IWL_FW_INI_TIME_POINT_NUM)
1044                return;
1045
1046        hcmd_list = &fwrt->trans->dbg.time_point[tp_id].hcmd_list;
1047        trig_list = &fwrt->trans->dbg.time_point[tp_id].active_trig_list;
1048
1049        switch (tp_id) {
1050        case IWL_FW_INI_TIME_POINT_EARLY:
1051                iwl_dbg_tlv_init_cfg(fwrt);
1052                iwl_dbg_tlv_tp_trigger(fwrt, trig_list, tp_data, NULL);
1053                break;
1054        case IWL_FW_INI_TIME_POINT_AFTER_ALIVE:
1055                iwl_dbg_tlv_apply_buffers(fwrt);
1056                iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);
1057                iwl_dbg_tlv_tp_trigger(fwrt, trig_list, tp_data, NULL);
1058                break;
1059        case IWL_FW_INI_TIME_POINT_PERIODIC:
1060                iwl_dbg_tlv_set_periodic_trigs(fwrt);
1061                iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);
1062                break;
1063        case IWL_FW_INI_TIME_POINT_FW_RSP_OR_NOTIF:
1064        case IWL_FW_INI_TIME_POINT_MISSED_BEACONS:
1065        case IWL_FW_INI_TIME_POINT_FW_DHC_NOTIFICATION:
1066                iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);
1067                iwl_dbg_tlv_tp_trigger(fwrt, trig_list, tp_data,
1068                                       iwl_dbg_tlv_check_fw_pkt);
1069                break;
1070        default:
1071                iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);
1072                iwl_dbg_tlv_tp_trigger(fwrt, trig_list, tp_data, NULL);
1073                break;
1074        }
1075}
1076IWL_EXPORT_SYMBOL(iwl_dbg_tlv_time_point);
1077