Skip to content

Commit 490fefe

Browse files
lkaufman-heegrumbach
authored andcommitted
iwlwifi: define the .ucode file format for debug
Debug information can be appended to the firmware file. This information will be used by the driver to enable / disable debugging features in the firmware. Signed-off-by: Liad Kaufman <liad.kaufman@intel.com> Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
1 parent b4c82ad commit 490fefe

File tree

3 files changed

+285
-15
lines changed

3 files changed

+285
-15
lines changed

drivers/net/wireless/iwlwifi/iwl-drv.c

+106-15
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,11 @@ static void iwl_free_fw_img(struct iwl_drv *drv, struct fw_img *img)
184184
static void iwl_dealloc_ucode(struct iwl_drv *drv)
185185
{
186186
int i;
187+
188+
kfree(drv->fw.dbg_dest_tlv);
189+
for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++)
190+
kfree(drv->fw.dbg_conf_tlv[i]);
191+
187192
for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
188193
iwl_free_fw_img(drv, drv->fw.img + i);
189194
}
@@ -308,6 +313,11 @@ struct iwl_firmware_pieces {
308313

309314
u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr;
310315
u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr;
316+
317+
/* FW debug data parsed for driver usage */
318+
struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
319+
struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX];
320+
size_t dbg_conf_tlv_len[FW_DBG_MAX];
311321
};
312322

313323
/*
@@ -853,6 +863,58 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
853863
capa->n_scan_channels =
854864
le32_to_cpup((__le32 *)tlv_data);
855865
break;
866+
case IWL_UCODE_TLV_FW_DBG_DEST: {
867+
struct iwl_fw_dbg_dest_tlv *dest = (void *)tlv_data;
868+
869+
if (pieces->dbg_dest_tlv) {
870+
IWL_ERR(drv,
871+
"dbg destination ignored, already exists\n");
872+
break;
873+
}
874+
875+
pieces->dbg_dest_tlv = dest;
876+
IWL_INFO(drv, "Found debug destination: %s\n",
877+
get_fw_dbg_mode_string(dest->monitor_mode));
878+
879+
drv->fw.dbg_dest_reg_num =
880+
tlv_len - offsetof(struct iwl_fw_dbg_dest_tlv,
881+
reg_ops);
882+
drv->fw.dbg_dest_reg_num /=
883+
sizeof(drv->fw.dbg_dest_tlv->reg_ops[0]);
884+
885+
break;
886+
}
887+
case IWL_UCODE_TLV_FW_DBG_CONF: {
888+
struct iwl_fw_dbg_conf_tlv *conf = (void *)tlv_data;
889+
890+
if (!pieces->dbg_dest_tlv) {
891+
IWL_ERR(drv,
892+
"Ignore dbg config %d - no destination configured\n",
893+
conf->id);
894+
break;
895+
}
896+
897+
if (conf->id >= ARRAY_SIZE(drv->fw.dbg_conf_tlv)) {
898+
IWL_ERR(drv,
899+
"Skip unknown configuration: %d\n",
900+
conf->id);
901+
break;
902+
}
903+
904+
if (pieces->dbg_conf_tlv[conf->id]) {
905+
IWL_ERR(drv,
906+
"Ignore duplicate dbg config %d\n",
907+
conf->id);
908+
break;
909+
}
910+
911+
IWL_INFO(drv, "Found debug configuration: %d\n",
912+
conf->id);
913+
914+
pieces->dbg_conf_tlv[conf->id] = conf;
915+
pieces->dbg_conf_tlv_len[conf->id] = tlv_len;
916+
break;
917+
}
856918
default:
857919
IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
858920
break;
@@ -996,7 +1058,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
9961058
struct iwl_ucode_header *ucode;
9971059
struct iwlwifi_opmode_table *op;
9981060
int err;
999-
struct iwl_firmware_pieces pieces;
1061+
struct iwl_firmware_pieces *pieces;
10001062
const unsigned int api_max = drv->cfg->ucode_api_max;
10011063
unsigned int api_ok = drv->cfg->ucode_api_ok;
10021064
const unsigned int api_min = drv->cfg->ucode_api_min;
@@ -1013,7 +1075,9 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
10131075
if (!api_ok)
10141076
api_ok = api_max;
10151077

1016-
memset(&pieces, 0, sizeof(pieces));
1078+
pieces = kzalloc(sizeof(*pieces), GFP_KERNEL);
1079+
if (!pieces)
1080+
return;
10171081

10181082
if (!ucode_raw) {
10191083
if (drv->fw_index <= api_ok)
@@ -1036,10 +1100,10 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
10361100
ucode = (struct iwl_ucode_header *)ucode_raw->data;
10371101

10381102
if (ucode->ver)
1039-
err = iwl_parse_v1_v2_firmware(drv, ucode_raw, &pieces);
1103+
err = iwl_parse_v1_v2_firmware(drv, ucode_raw, pieces);
10401104
else
1041-
err = iwl_parse_tlv_firmware(drv, ucode_raw, &pieces,
1042-
&fw->ucode_capa);
1105+
err = iwl_parse_tlv_firmware(drv, ucode_raw, pieces,
1106+
&fw->ucode_capa);
10431107

10441108
if (err)
10451109
goto try_again;
@@ -1079,7 +1143,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
10791143
* In mvm uCode there is no difference between data and instructions
10801144
* sections.
10811145
*/
1082-
if (!fw->mvm_fw && validate_sec_sizes(drv, &pieces, drv->cfg))
1146+
if (!fw->mvm_fw && validate_sec_sizes(drv, pieces, drv->cfg))
10831147
goto try_again;
10841148

10851149
/* Allocate ucode buffers for card's bus-master loading ... */
@@ -1088,30 +1152,54 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
10881152
* 1) unmodified from disk
10891153
* 2) backup cache for save/restore during power-downs */
10901154
for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
1091-
if (iwl_alloc_ucode(drv, &pieces, i))
1155+
if (iwl_alloc_ucode(drv, pieces, i))
10921156
goto out_free_fw;
10931157

1158+
if (pieces->dbg_dest_tlv) {
1159+
drv->fw.dbg_dest_tlv =
1160+
kmemdup(pieces->dbg_dest_tlv,
1161+
sizeof(*pieces->dbg_dest_tlv) +
1162+
sizeof(pieces->dbg_dest_tlv->reg_ops[0]) *
1163+
drv->fw.dbg_dest_reg_num, GFP_KERNEL);
1164+
1165+
if (!drv->fw.dbg_dest_tlv)
1166+
goto out_free_fw;
1167+
}
1168+
1169+
for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++) {
1170+
if (pieces->dbg_conf_tlv[i]) {
1171+
drv->fw.dbg_conf_tlv_len[i] =
1172+
pieces->dbg_conf_tlv_len[i];
1173+
drv->fw.dbg_conf_tlv[i] =
1174+
kmemdup(pieces->dbg_conf_tlv[i],
1175+
drv->fw.dbg_conf_tlv_len[i],
1176+
GFP_KERNEL);
1177+
if (!drv->fw.dbg_conf_tlv[i])
1178+
goto out_free_fw;
1179+
}
1180+
}
1181+
10941182
/* Now that we can no longer fail, copy information */
10951183

10961184
/*
10971185
* The (size - 16) / 12 formula is based on the information recorded
10981186
* for each event, which is of mode 1 (including timestamp) for all
10991187
* new microcodes that include this information.
11001188
*/
1101-
fw->init_evtlog_ptr = pieces.init_evtlog_ptr;
1102-
if (pieces.init_evtlog_size)
1103-
fw->init_evtlog_size = (pieces.init_evtlog_size - 16)/12;
1189+
fw->init_evtlog_ptr = pieces->init_evtlog_ptr;
1190+
if (pieces->init_evtlog_size)
1191+
fw->init_evtlog_size = (pieces->init_evtlog_size - 16)/12;
11041192
else
11051193
fw->init_evtlog_size =
11061194
drv->cfg->base_params->max_event_log_size;
1107-
fw->init_errlog_ptr = pieces.init_errlog_ptr;
1108-
fw->inst_evtlog_ptr = pieces.inst_evtlog_ptr;
1109-
if (pieces.inst_evtlog_size)
1110-
fw->inst_evtlog_size = (pieces.inst_evtlog_size - 16)/12;
1195+
fw->init_errlog_ptr = pieces->init_errlog_ptr;
1196+
fw->inst_evtlog_ptr = pieces->inst_evtlog_ptr;
1197+
if (pieces->inst_evtlog_size)
1198+
fw->inst_evtlog_size = (pieces->inst_evtlog_size - 16)/12;
11111199
else
11121200
fw->inst_evtlog_size =
11131201
drv->cfg->base_params->max_event_log_size;
1114-
fw->inst_errlog_ptr = pieces.inst_errlog_ptr;
1202+
fw->inst_errlog_ptr = pieces->inst_errlog_ptr;
11151203

11161204
/*
11171205
* figure out the offset of chain noise reset and gain commands
@@ -1213,10 +1301,12 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
12131301
iwl_req_fw_callback(ucode_raw, context);
12141302
}
12151303

1304+
kfree(pieces);
12161305
return;
12171306

12181307
try_again:
12191308
/* try next, if any */
1309+
kfree(pieces);
12201310
release_firmware(ucode_raw);
12211311
if (iwl_request_firmware(drv, false))
12221312
goto out_unbind;
@@ -1227,6 +1317,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
12271317
iwl_dealloc_ucode(drv);
12281318
release_firmware(ucode_raw);
12291319
out_unbind:
1320+
kfree(pieces);
12301321
complete(&drv->request_firmware_complete);
12311322
device_release_driver(drv->trans->dev);
12321323
}

drivers/net/wireless/iwlwifi/iwl-fw-file.h

+124
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ enum iwl_ucode_tlv_type {
131131
IWL_UCODE_TLV_API_CHANGES_SET = 29,
132132
IWL_UCODE_TLV_ENABLED_CAPABILITIES = 30,
133133
IWL_UCODE_TLV_N_SCAN_CHANNELS = 31,
134+
IWL_UCODE_TLV_FW_DBG_DEST = 38,
135+
IWL_UCODE_TLV_FW_DBG_CONF = 39,
134136
};
135137

136138
struct iwl_ucode_tlv {
@@ -362,4 +364,126 @@ struct iwl_fw_cipher_scheme {
362364
u8 hw_cipher;
363365
} __packed;
364366

367+
enum iwl_fw_dbg_reg_operator {
368+
CSR_ASSIGN,
369+
CSR_SETBIT,
370+
CSR_CLEARBIT,
371+
372+
PRPH_ASSIGN,
373+
PRPH_SETBIT,
374+
PRPH_CLEARBIT,
375+
};
376+
377+
/**
378+
* struct iwl_fw_dbg_reg_op - an operation on a register
379+
*
380+
* @op: %enum iwl_fw_dbg_reg_operator
381+
* @addr: offset of the register
382+
* @val: value
383+
*/
384+
struct iwl_fw_dbg_reg_op {
385+
u8 op;
386+
u8 reserved[3];
387+
__le32 addr;
388+
__le32 val;
389+
} __packed;
390+
391+
/**
392+
* enum iwl_fw_dbg_monitor_mode - available monitor recording modes
393+
*
394+
* @SMEM_MODE: monitor stores the data in SMEM
395+
* @EXTERNAL_MODE: monitor stores the data in allocated DRAM
396+
* @MARBH_MODE: monitor stores the data in MARBH buffer
397+
*/
398+
enum iwl_fw_dbg_monitor_mode {
399+
SMEM_MODE = 0,
400+
EXTERNAL_MODE = 1,
401+
MARBH_MODE = 2,
402+
};
403+
404+
/**
405+
* struct iwl_fw_dbg_dest_tlv - configures the destination of the debug data
406+
*
407+
* @version: version of the TLV - currently 0
408+
* @monitor_mode: %enum iwl_fw_dbg_monitor_mode
409+
* @base_reg: addr of the base addr register (PRPH)
410+
* @end_reg: addr of the end addr register (PRPH)
411+
* @write_ptr_reg: the addr of the reg of the write pointer
412+
* @wrap_count: the addr of the reg of the wrap_count
413+
* @base_shift: shift right of the base addr reg
414+
* @end_shift: shift right of the end addr reg
415+
* @reg_ops: array of registers operations
416+
*
417+
* This parses IWL_UCODE_TLV_FW_DBG_DEST
418+
*/
419+
struct iwl_fw_dbg_dest_tlv {
420+
u8 version;
421+
u8 monitor_mode;
422+
u8 reserved[2];
423+
__le32 base_reg;
424+
__le32 end_reg;
425+
__le32 write_ptr_reg;
426+
__le32 wrap_count;
427+
u8 base_shift;
428+
u8 end_shift;
429+
struct iwl_fw_dbg_reg_op reg_ops[0];
430+
} __packed;
431+
432+
struct iwl_fw_dbg_conf_hcmd {
433+
u8 id;
434+
u8 reserved;
435+
__le16 len;
436+
u8 data[0];
437+
} __packed;
438+
439+
/**
440+
* struct iwl_fw_dbg_trigger - a TLV that describes a debug configuration
441+
*
442+
* @enabled: is this trigger enabled
443+
* @reserved:
444+
* @len: length, in bytes, of the %trigger field
445+
* @trigger: pointer to a trigger struct
446+
*/
447+
struct iwl_fw_dbg_trigger {
448+
u8 enabled;
449+
u8 reserved;
450+
u8 len;
451+
u8 trigger[0];
452+
} __packed;
453+
454+
/**
455+
* enum iwl_fw_dbg_conf - configurations available
456+
*
457+
* @FW_DBG_CUSTOM: take this configuration from alive
458+
* Note that the trigger is NO-OP for this configuration
459+
*/
460+
enum iwl_fw_dbg_conf {
461+
FW_DBG_CUSTOM = 0,
462+
463+
/* must be last */
464+
FW_DBG_MAX,
465+
FW_DBG_INVALID = 0xff,
466+
};
467+
468+
/**
469+
* struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration
470+
*
471+
* @id: %enum iwl_fw_dbg_conf
472+
* @usniffer: should the uSniffer image be used
473+
* @num_of_hcmds: how many HCMDs to send are present here
474+
* @hcmd: a variable length host command to be sent to apply the configuration.
475+
* If there is more than one HCMD to send, they will appear one after the
476+
* other and be sent in the order that they appear in.
477+
* This parses IWL_UCODE_TLV_FW_DBG_CONF
478+
*/
479+
struct iwl_fw_dbg_conf_tlv {
480+
u8 id;
481+
u8 usniffer;
482+
u8 reserved;
483+
u8 num_of_hcmds;
484+
struct iwl_fw_dbg_conf_hcmd hcmd;
485+
486+
/* struct iwl_fw_dbg_trigger sits after all variable length hcmds */
487+
} __packed;
488+
365489
#endif /* __iwl_fw_file_h__ */

0 commit comments

Comments
 (0)