Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

wimax/i2400m: on firmware upload, select BCF header that matches device's request

Devices based on the i2400m emit a "barker" (32 bit unsigned) when
they boot. This barker is used to select, in the firmware file image,
which header should be used to process the rest of the file.

This commit implements said support, completing the series started by
previous commits.

We modify the i2400m_fw_dnload() firmware loading path by adding a
call to i2400m_bcf_hdr_find() [new function], in which the right BCF
header [as listed in i2400m->fw_hdrs by i2400m_fw_check()] is
located. Then this header is fed to i2400m_dnload_init() and
i2400m_dnload_finalize().

The changes to i2400m_dnload_finalize() are smaller than they look;
they add the bcf_hdr argument and use that instead of bcf. Likewise in
i2400m_dnload_init().

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>

+92 -19
+92 -19
drivers/net/wimax/i2400m/fw.c
··· 665 665 * Download a BCF file's sections to the device 666 666 * 667 667 * @i2400m: device descriptor 668 - * @bcf: pointer to firmware data (followed by the payloads). Assumed 669 - * verified and consistent. 668 + * @bcf: pointer to firmware data (first header followed by the 669 + * payloads). Assumed verified and consistent. 670 670 * @bcf_len: length (in bytes) of the @bcf buffer. 671 671 * 672 672 * Returns: < 0 errno code on error or the offset to the jump instruction. ··· 756 756 /* 757 757 * Do the final steps of uploading firmware 758 758 * 759 + * @bcf_hdr: BCF header we are actually using 760 + * @bcf: pointer to the firmware image (which matches the first header 761 + * that is followed by the actual payloads). 762 + * @offset: [byte] offset into @bcf for the command we need to send. 763 + * 759 764 * Depending on the boot mode (signed vs non-signed), different 760 765 * actions need to be taken. 761 766 */ 762 767 static 763 768 int i2400m_dnload_finalize(struct i2400m *i2400m, 769 + const struct i2400m_bcf_hdr *bcf_hdr, 764 770 const struct i2400m_bcf_hdr *bcf, size_t offset) 765 771 { 766 772 int ret = 0; ··· 798 792 cmd_buf = i2400m->bm_cmd_buf; 799 793 memcpy(&cmd_buf->cmd, cmd, sizeof(*cmd)); 800 794 signature_block_offset = 801 - sizeof(*bcf) 802 - + le32_to_cpu(bcf->key_size) * sizeof(u32) 803 - + le32_to_cpu(bcf->exponent_size) * sizeof(u32); 795 + sizeof(*bcf_hdr) 796 + + le32_to_cpu(bcf_hdr->key_size) * sizeof(u32) 797 + + le32_to_cpu(bcf_hdr->exponent_size) * sizeof(u32); 804 798 signature_block_size = 805 - le32_to_cpu(bcf->modulus_size) * sizeof(u32); 806 - memcpy(cmd_buf->cmd_pl, (void *) bcf + signature_block_offset, 799 + le32_to_cpu(bcf_hdr->modulus_size) * sizeof(u32); 800 + memcpy(cmd_buf->cmd_pl, 801 + (void *) bcf_hdr + signature_block_offset, 807 802 signature_block_size); 808 803 ret = i2400m_bm_cmd(i2400m, &cmd_buf->cmd, 809 804 sizeof(cmd_buf->cmd) + signature_block_size, ··· 1129 1122 * (signed or non-signed). 1130 1123 */ 1131 1124 static 1132 - int i2400m_dnload_init(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf) 1125 + int i2400m_dnload_init(struct i2400m *i2400m, 1126 + const struct i2400m_bcf_hdr *bcf_hdr) 1133 1127 { 1134 1128 int result; 1135 1129 struct device *dev = i2400m_dev(i2400m); 1136 1130 1137 1131 if (i2400m_boot_is_signed(i2400m)) { 1138 1132 d_printf(1, dev, "signed boot\n"); 1139 - result = i2400m_dnload_init_signed(i2400m, bcf); 1133 + result = i2400m_dnload_init_signed(i2400m, bcf_hdr); 1140 1134 if (result == -ERESTARTSYS) 1141 1135 return result; 1142 1136 if (result < 0) ··· 1190 1182 date = le32_to_cpu(bcf_hdr->date); 1191 1183 size = sizeof(u32) * le32_to_cpu(bcf_hdr->size); 1192 1184 1193 - d_printf(1, dev, "firmware %s #%d@%08x: BCF header " 1194 - "type:vendor:id 0x%x:%x:%x v%u.%u (%zu/%zu B) built %08x\n", 1185 + d_printf(1, dev, "firmware %s #%zd@%08zx: BCF header " 1186 + "type:vendor:id 0x%x:%x:%x v%u.%u (%u/%u B) built %08x\n", 1195 1187 i2400m->fw_name, index, offset, 1196 1188 module_type, module_vendor, module_id, 1197 1189 major_version, minor_version, header_len, size, date); 1198 1190 1199 1191 /* Hard errors */ 1200 1192 if (major_version != 1) { 1201 - dev_err(dev, "firmware %s #%d@%08x: major header version " 1193 + dev_err(dev, "firmware %s #%zd@%08zx: major header version " 1202 1194 "v%u.%u not supported\n", 1203 1195 i2400m->fw_name, index, offset, 1204 1196 major_version, minor_version); ··· 1206 1198 } 1207 1199 1208 1200 if (module_type != 6) { /* built for the right hardware? */ 1209 - dev_err(dev, "firmware %s #%d@%08x: unexpected module " 1201 + dev_err(dev, "firmware %s #%zd@%08zx: unexpected module " 1210 1202 "type 0x%x; aborting\n", 1211 1203 i2400m->fw_name, index, offset, 1212 1204 module_type); ··· 1214 1206 } 1215 1207 1216 1208 if (module_vendor != 0x8086) { 1217 - dev_err(dev, "firmware %s #%d@%08x: unexpected module " 1209 + dev_err(dev, "firmware %s #%zd@%08zx: unexpected module " 1218 1210 "vendor 0x%x; aborting\n", 1219 1211 i2400m->fw_name, index, offset, module_vendor); 1220 1212 return -EBADF; 1221 1213 } 1222 1214 1223 1215 if (date < 0x20080300) 1224 - dev_warn(dev, "firmware %s #%d@%08x: build date %08x " 1216 + dev_warn(dev, "firmware %s #%zd@%08zx: build date %08x " 1225 1217 "too old; unsupported\n", 1226 1218 i2400m->fw_name, index, offset, date); 1227 1219 return 0; ··· 1247 1239 size_t headers = 0; 1248 1240 const struct i2400m_bcf_hdr *bcf_hdr; 1249 1241 const void *itr, *next, *top; 1250 - unsigned slots = 0, used_slots = 0; 1242 + size_t slots = 0, used_slots = 0; 1251 1243 1252 1244 for (itr = bcf, top = itr + bcf_size; 1253 1245 itr < top; ··· 1257 1249 leftover = top - itr; 1258 1250 offset = itr - (const void *) bcf; 1259 1251 if (leftover <= sizeof(*bcf_hdr)) { 1260 - dev_err(dev, "firmware %s: %zu B left at @%x, " 1252 + dev_err(dev, "firmware %s: %zu B left at @%zx, " 1261 1253 "not enough for BCF header\n", 1262 1254 i2400m->fw_name, leftover, offset); 1263 1255 break; ··· 1301 1293 1302 1294 1303 1295 /* 1296 + * Match a barker to a BCF header module ID 1297 + * 1298 + * The device sends a barker which tells the firmware loader which 1299 + * header in the BCF file has to be used. This does the matching. 1300 + */ 1301 + static 1302 + unsigned i2400m_bcf_hdr_match(struct i2400m *i2400m, 1303 + const struct i2400m_bcf_hdr *bcf_hdr) 1304 + { 1305 + u32 barker = le32_to_cpu(i2400m->barker->data[0]) 1306 + & 0x7fffffff; 1307 + u32 module_id = le32_to_cpu(bcf_hdr->module_id) 1308 + & 0x7fffffff; /* high bit used for something else */ 1309 + 1310 + /* special case for 5x50 */ 1311 + if (barker == I2400M_SBOOT_BARKER && module_id == 0) 1312 + return 1; 1313 + if (module_id == barker) 1314 + return 1; 1315 + return 0; 1316 + } 1317 + 1318 + static 1319 + const struct i2400m_bcf_hdr *i2400m_bcf_hdr_find(struct i2400m *i2400m) 1320 + { 1321 + struct device *dev = i2400m_dev(i2400m); 1322 + const struct i2400m_bcf_hdr **bcf_itr, *bcf_hdr; 1323 + unsigned i = 0; 1324 + u32 barker = le32_to_cpu(i2400m->barker->data[0]); 1325 + 1326 + d_printf(2, dev, "finding BCF header for barker %08x\n", barker); 1327 + if (barker == I2400M_NBOOT_BARKER) { 1328 + bcf_hdr = i2400m->fw_hdrs[0]; 1329 + d_printf(1, dev, "using BCF header #%u/%08x for non-signed " 1330 + "barker\n", 0, le32_to_cpu(bcf_hdr->module_id)); 1331 + return bcf_hdr; 1332 + } 1333 + for (bcf_itr = i2400m->fw_hdrs; *bcf_itr != NULL; bcf_itr++, i++) { 1334 + bcf_hdr = *bcf_itr; 1335 + if (i2400m_bcf_hdr_match(i2400m, bcf_hdr)) { 1336 + d_printf(1, dev, "hit on BCF hdr #%u/%08x\n", 1337 + i, le32_to_cpu(bcf_hdr->module_id)); 1338 + return bcf_hdr; 1339 + } else 1340 + d_printf(1, dev, "miss on BCF hdr #%u/%08x\n", 1341 + i, le32_to_cpu(bcf_hdr->module_id)); 1342 + } 1343 + dev_err(dev, "cannot find a matching BCF header for barker %08x\n", 1344 + barker); 1345 + return NULL; 1346 + } 1347 + 1348 + 1349 + /* 1304 1350 * Download the firmware to the device 1305 1351 * 1306 1352 * @i2400m: device descriptor ··· 1375 1313 int ret = 0; 1376 1314 struct device *dev = i2400m_dev(i2400m); 1377 1315 int count = i2400m->bus_bm_retries; 1316 + const struct i2400m_bcf_hdr *bcf_hdr; 1378 1317 1379 1318 d_fnstart(5, dev, "(i2400m %p bcf %p size %zu)\n", 1380 1319 i2400m, bcf, bcf_size); ··· 1400 1337 * Initialize the download, push the bytes to the device and 1401 1338 * then jump to the new firmware. Note @ret is passed with the 1402 1339 * offset of the jump instruction to _dnload_finalize() 1340 + * 1341 + * Note we need to use the BCF header in the firmware image 1342 + * that matches the barker that the device sent when it 1343 + * rebooted, so it has to be passed along. 1403 1344 */ 1404 - ret = i2400m_dnload_init(i2400m, bcf); /* Init device's dnload */ 1345 + ret = -EBADF; 1346 + bcf_hdr = i2400m_bcf_hdr_find(i2400m); 1347 + if (bcf_hdr == NULL) 1348 + goto error_bcf_hdr_find; 1349 + 1350 + ret = i2400m_dnload_init(i2400m, bcf_hdr); 1405 1351 if (ret == -ERESTARTSYS) 1406 1352 goto error_dev_rebooted; 1407 1353 if (ret < 0) ··· 1425 1353 goto error_dnload_bcf; 1426 1354 } 1427 1355 1428 - ret = i2400m_dnload_finalize(i2400m, bcf, ret); 1356 + ret = i2400m_dnload_finalize(i2400m, bcf_hdr, bcf, ret); 1429 1357 if (ret == -ERESTARTSYS) 1430 1358 goto error_dev_rebooted; 1431 1359 if (ret < 0) { ··· 1442 1370 error_dnload_finalize: 1443 1371 error_dnload_bcf: 1444 1372 error_dnload_init: 1373 + error_bcf_hdr_find: 1445 1374 error_bootrom_init: 1446 1375 error_too_many_reboots: 1447 1376 d_fnend(5, dev, "(i2400m %p bcf %p size %zu) = %d\n",