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