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

wimax/i2400m: support extended firmware format

The SBCF firmware format has been extended to support extra headers
after the main payload. These extra headers are used to sign the
firmware code with more than one certificate. This eases up
distributing single code images that work in more than one SKU of the
device.

The changes to support this feature will be spread in a series of
commits. This one just adds the support to parse the extra headers and
store them in i2400m->fw_hdrs. Coming changes to the loader code will
use that to determine which header to upload to the device.

The i2400m_fw_check() function now iterates over all the headers and
for each, calls i2400m_fw_hdr_check(), which does some basic checks on
each header. It then stores the headers for the bootloader code to use.

The i2400m_dev_bootstrap() function has been modified to cleanup
i2400m->fw_hdrs when done.

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

+134 -59
+130 -59
drivers/net/wimax/i2400m/fw.c
··· 78 78 * 79 79 * We can then upload the firmware file. The file is composed of a BCF 80 80 * header (basic data, keys and signatures) and a list of write 81 - * commands and payloads. We first upload the header 82 - * [i2400m_dnload_init()] and then pass the commands and payloads 83 - * verbatim to the i2400m_bm_cmd() function 84 - * [i2400m_dnload_bcf()]. Then we tell the device to jump to the new 85 - * firmware [i2400m_dnload_finalize()]. 81 + * commands and payloads. Optionally more BCF headers might follow the 82 + * main payload. We first upload the header [i2400m_dnload_init()] and 83 + * then pass the commands and payloads verbatim to the i2400m_bm_cmd() 84 + * function [i2400m_dnload_bcf()]. Then we tell the device to jump to 85 + * the new firmware [i2400m_dnload_finalize()]. 86 86 * 87 87 * Once firmware is uploaded, we are good to go :) 88 88 * ··· 115 115 * i2400m_dev_bootstrap Called by __i2400m_dev_start() 116 116 * request_firmware 117 117 * i2400m_fw_check 118 + * i2400m_fw_hdr_check 118 119 * i2400m_fw_dnload 119 120 * release_firmware 120 121 * ··· 1152 1151 1153 1152 1154 1153 /* 1155 - * Run quick consistency tests on the firmware file 1154 + * Run consistency tests on the firmware file and load up headers 1156 1155 * 1157 1156 * Check for the firmware being made for the i2400m device, 1158 1157 * etc...These checks are mostly informative, as the device will make 1159 1158 * them too; but the driver's response is more informative on what 1160 1159 * went wrong. 1160 + * 1161 + * This will also look at all the headers present on the firmware 1162 + * file, and update i2400m->fw_bcf_hdr to point to them. 1161 1163 */ 1162 1164 static 1163 - int i2400m_fw_check(struct i2400m *i2400m, 1164 - const struct i2400m_bcf_hdr *bcf, 1165 - size_t bcf_size) 1165 + int i2400m_fw_hdr_check(struct i2400m *i2400m, 1166 + const struct i2400m_bcf_hdr *bcf_hdr, 1167 + size_t index, size_t offset) 1166 1168 { 1167 - int result; 1168 1169 struct device *dev = i2400m_dev(i2400m); 1170 + 1169 1171 unsigned module_type, header_len, major_version, minor_version, 1170 1172 module_id, module_vendor, date, size; 1171 1173 1172 - /* Check hard errors */ 1173 - result = -EINVAL; 1174 - if (bcf_size < sizeof(*bcf)) { /* big enough header? */ 1175 - dev_err(dev, "firmware %s too short: " 1176 - "%zu B vs %zu (at least) expected\n", 1177 - i2400m->fw_name, bcf_size, sizeof(*bcf)); 1178 - goto error; 1174 + module_type = bcf_hdr->module_type; 1175 + header_len = sizeof(u32) * le32_to_cpu(bcf_hdr->header_len); 1176 + major_version = (le32_to_cpu(bcf_hdr->header_version) & 0xffff0000) 1177 + >> 16; 1178 + minor_version = le32_to_cpu(bcf_hdr->header_version) & 0x0000ffff; 1179 + module_id = le32_to_cpu(bcf_hdr->module_id); 1180 + module_vendor = le32_to_cpu(bcf_hdr->module_vendor); 1181 + date = le32_to_cpu(bcf_hdr->date); 1182 + size = sizeof(u32) * le32_to_cpu(bcf_hdr->size); 1183 + 1184 + d_printf(1, dev, "firmware %s #%d@%08x: BCF header " 1185 + "type:vendor:id 0x%x:%x:%x v%u.%u (%zu/%zu B) built %08x\n", 1186 + i2400m->fw_name, index, offset, 1187 + module_type, module_vendor, module_id, 1188 + major_version, minor_version, header_len, size, date); 1189 + 1190 + /* Hard errors */ 1191 + if (major_version != 1) { 1192 + dev_err(dev, "firmware %s #%d@%08x: major header version " 1193 + "v%u.%u not supported\n", 1194 + i2400m->fw_name, index, offset, 1195 + major_version, minor_version); 1196 + return -EBADF; 1179 1197 } 1180 - 1181 - module_type = bcf->module_type; 1182 - header_len = sizeof(u32) * le32_to_cpu(bcf->header_len); 1183 - major_version = (le32_to_cpu(bcf->header_version) & 0xffff0000) >> 16; 1184 - minor_version = le32_to_cpu(bcf->header_version) & 0x0000ffff; 1185 - module_id = le32_to_cpu(bcf->module_id); 1186 - module_vendor = le32_to_cpu(bcf->module_vendor); 1187 - date = le32_to_cpu(bcf->date); 1188 - size = sizeof(u32) * le32_to_cpu(bcf->size); 1189 - 1190 - if (bcf_size != size) { /* annoyingly paranoid */ 1191 - dev_err(dev, "firmware %s: bad size, got " 1192 - "%zu B vs %u expected\n", 1193 - i2400m->fw_name, bcf_size, size); 1194 - goto error; 1195 - } 1196 - 1197 - d_printf(2, dev, "type 0x%x id 0x%x vendor 0x%x; header v%u.%u (%zu B) " 1198 - "date %08x (%zu B)\n", 1199 - module_type, module_id, module_vendor, 1200 - major_version, minor_version, (size_t) header_len, 1201 - date, (size_t) size); 1202 1198 1203 1199 if (module_type != 6) { /* built for the right hardware? */ 1204 - dev_err(dev, "bad fw %s: unexpected module type 0x%x; " 1205 - "aborting\n", i2400m->fw_name, module_type); 1206 - goto error; 1200 + dev_err(dev, "firmware %s #%d@%08x: unexpected module " 1201 + "type 0x%x; aborting\n", 1202 + i2400m->fw_name, index, offset, 1203 + module_type); 1204 + return -EBADF; 1207 1205 } 1208 1206 1209 - if (major_version != 1) { 1210 - dev_err(dev, "%s: major header version v%u.%u not supported\n", 1211 - i2400m->fw_name, major_version, minor_version); 1212 - goto error; 1207 + if (module_vendor != 0x8086) { 1208 + dev_err(dev, "firmware %s #%d@%08x: unexpected module " 1209 + "vendor 0x%x; aborting\n", 1210 + i2400m->fw_name, index, offset, module_vendor); 1211 + return -EBADF; 1213 1212 } 1214 1213 1215 - /* Check soft-er errors */ 1216 - result = 0; 1217 - if (module_vendor != 0x8086) 1218 - dev_err(dev, "bad fw %s? unexpected vendor 0x%04x\n", 1219 - i2400m->fw_name, module_vendor); 1220 1214 if (date < 0x20080300) 1221 - dev_err(dev, "bad fw %s? build date too old %08x\n", 1222 - i2400m->fw_name, date); 1223 - error: 1215 + dev_warn(dev, "firmware %s #%d@%08x: build date %08x " 1216 + "too old; unsupported\n", 1217 + i2400m->fw_name, index, offset, date); 1218 + return 0; 1219 + } 1220 + 1221 + 1222 + /* 1223 + * Run consistency tests on the firmware file and load up headers 1224 + * 1225 + * Check for the firmware being made for the i2400m device, 1226 + * etc...These checks are mostly informative, as the device will make 1227 + * them too; but the driver's response is more informative on what 1228 + * went wrong. 1229 + * 1230 + * This will also look at all the headers present on the firmware 1231 + * file, and update i2400m->fw_hdrs to point to them. 1232 + */ 1233 + static 1234 + int i2400m_fw_check(struct i2400m *i2400m, const void *bcf, size_t bcf_size) 1235 + { 1236 + int result; 1237 + struct device *dev = i2400m_dev(i2400m); 1238 + size_t headers = 0; 1239 + const struct i2400m_bcf_hdr *bcf_hdr; 1240 + const void *itr, *next, *top; 1241 + unsigned slots = 0, used_slots = 0; 1242 + 1243 + for (itr = bcf, top = itr + bcf_size; 1244 + itr < top; 1245 + headers++, itr = next) { 1246 + size_t leftover, offset, header_len, size; 1247 + 1248 + leftover = top - itr; 1249 + offset = itr - (const void *) bcf; 1250 + if (leftover <= sizeof(*bcf_hdr)) { 1251 + dev_err(dev, "firmware %s: %zu B left at @%x, " 1252 + "not enough for BCF header\n", 1253 + i2400m->fw_name, leftover, offset); 1254 + break; 1255 + } 1256 + bcf_hdr = itr; 1257 + /* Only the first header is supposed to be followed by 1258 + * payload */ 1259 + header_len = sizeof(u32) * le32_to_cpu(bcf_hdr->header_len); 1260 + size = sizeof(u32) * le32_to_cpu(bcf_hdr->size); 1261 + if (headers == 0) 1262 + next = itr + size; 1263 + else 1264 + next = itr + header_len; 1265 + 1266 + result = i2400m_fw_hdr_check(i2400m, bcf_hdr, headers, offset); 1267 + if (result < 0) 1268 + continue; 1269 + if (used_slots + 1 >= slots) { 1270 + /* +1 -> we need to account for the one we'll 1271 + * occupy and at least an extra one for 1272 + * always being NULL */ 1273 + result = i2400m_zrealloc_2x( 1274 + (void **) &i2400m->fw_hdrs, &slots, 1275 + sizeof(i2400m->fw_hdrs[0]), 1276 + GFP_KERNEL); 1277 + if (result < 0) 1278 + goto error_zrealloc; 1279 + } 1280 + i2400m->fw_hdrs[used_slots] = bcf_hdr; 1281 + used_slots++; 1282 + } 1283 + if (headers == 0) { 1284 + dev_err(dev, "firmware %s: no usable headers found\n", 1285 + i2400m->fw_name); 1286 + result = -EBADF; 1287 + } else 1288 + result = 0; 1289 + error_zrealloc: 1224 1290 return result; 1225 1291 } 1226 1292 ··· 1427 1359 bcf = (void *) fw->data; 1428 1360 i2400m->fw_name = fw_name; 1429 1361 ret = i2400m_fw_check(i2400m, bcf, fw->size); 1430 - if (ret >= 0) { 1362 + if (ret >= 0) 1431 1363 ret = i2400m_fw_dnload(i2400m, bcf, fw->size, flags); 1432 - if (ret >= 0) 1433 - break; 1434 - } else 1435 - dev_err(dev, "%s: cannot use, skipping\n", fw_name); 1364 + if (ret < 0) 1365 + dev_err(dev, "%s: cannot use: %d, skipping\n", 1366 + fw_name, ret); 1367 + kfree(i2400m->fw_hdrs); 1368 + i2400m->fw_hdrs = NULL; 1436 1369 release_firmware(fw); 1370 + if (ret >= 0) /* firmware loaded succesfully */ 1371 + break; 1437 1372 } 1438 1373 d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret); 1439 1374 return ret;
+4
drivers/net/wimax/i2400m/i2400m.h
··· 421 421 * @fw_version: version of the firmware interface, Major.minor, 422 422 * encoded in the high word and low word (major << 16 | minor). 423 423 * 424 + * @fw_hdrs: NULL terminated array of pointers to the firmware 425 + * headers. This is only available during firmware load time. 426 + * 424 427 * @barker: barker type that the device uses; this is initialized by 425 428 * i2400m_is_boot_barker() the first time it is called. Then it 426 429 * won't change during the life cycle of the device and everytime ··· 494 491 struct dentry *debugfs_dentry; 495 492 const char *fw_name; /* name of the current firmware image */ 496 493 unsigned long fw_version; /* version of the firmware interface */ 494 + const struct i2400m_bcf_hdr **fw_hdrs; 497 495 struct i2400m_barker_db *barker; 498 496 }; 499 497