aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wimax/i2400m/fw.c
diff options
context:
space:
mode:
authorInaky Perez-Gonzalez <inaky@linux.intel.com>2009-09-04 20:07:21 -0400
committerInaky Perez-Gonzalez <inaky@linux.intel.com>2009-10-19 02:55:57 -0400
commitbfc44187bbaeabf597be6566a24e8fa7d689b984 (patch)
treeaa7d6f28babc971950bf1e7589919c74f2ca6fa9 /drivers/net/wimax/i2400m/fw.c
parentfabce1a485dd985c0e4c16f61f4ddb5e27e49cbf (diff)
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>
Diffstat (limited to 'drivers/net/wimax/i2400m/fw.c')
-rw-r--r--drivers/net/wimax/i2400m/fw.c189
1 files changed, 130 insertions, 59 deletions
diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c
index 1fd2fee4c6c2..897e0be698c6 100644
--- a/drivers/net/wimax/i2400m/fw.c
+++ b/drivers/net/wimax/i2400m/fw.c
@@ -78,11 +78,11 @@
78 * 78 *
79 * We can then upload the firmware file. The file is composed of a BCF 79 * We can then upload the firmware file. The file is composed of a BCF
80 * header (basic data, keys and signatures) and a list of write 80 * header (basic data, keys and signatures) and a list of write
81 * commands and payloads. We first upload the header 81 * commands and payloads. Optionally more BCF headers might follow the
82 * [i2400m_dnload_init()] and then pass the commands and payloads 82 * main payload. We first upload the header [i2400m_dnload_init()] and
83 * verbatim to the i2400m_bm_cmd() function 83 * then pass the commands and payloads verbatim to the i2400m_bm_cmd()
84 * [i2400m_dnload_bcf()]. Then we tell the device to jump to the new 84 * function [i2400m_dnload_bcf()]. Then we tell the device to jump to
85 * firmware [i2400m_dnload_finalize()]. 85 * the new firmware [i2400m_dnload_finalize()].
86 * 86 *
87 * Once firmware is uploaded, we are good to go :) 87 * Once firmware is uploaded, we are good to go :)
88 * 88 *
@@ -115,6 +115,7 @@
115 * i2400m_dev_bootstrap Called by __i2400m_dev_start() 115 * i2400m_dev_bootstrap Called by __i2400m_dev_start()
116 * request_firmware 116 * request_firmware
117 * i2400m_fw_check 117 * i2400m_fw_check
118 * i2400m_fw_hdr_check
118 * i2400m_fw_dnload 119 * i2400m_fw_dnload
119 * release_firmware 120 * release_firmware
120 * 121 *
@@ -1151,75 +1152,142 @@ int i2400m_dnload_init(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf)
1151 1152
1152 1153
1153/* 1154/*
1154 * Run quick consistency tests on the firmware file 1155 * Run consistency tests on the firmware file and load up headers
1155 * 1156 *
1156 * Check for the firmware being made for the i2400m device, 1157 * Check for the firmware being made for the i2400m device,
1157 * etc...These checks are mostly informative, as the device will make 1158 * etc...These checks are mostly informative, as the device will make
1158 * them too; but the driver's response is more informative on what 1159 * them too; but the driver's response is more informative on what
1159 * went wrong. 1160 * went wrong.
1161 *
1162 * This will also look at all the headers present on the firmware
1163 * file, and update i2400m->fw_bcf_hdr to point to them.
1160 */ 1164 */
1161static 1165static
1162int i2400m_fw_check(struct i2400m *i2400m, 1166int i2400m_fw_hdr_check(struct i2400m *i2400m,
1163 const struct i2400m_bcf_hdr *bcf, 1167 const struct i2400m_bcf_hdr *bcf_hdr,
1164 size_t bcf_size) 1168 size_t index, size_t offset)
1165{ 1169{
1166 int result;
1167 struct device *dev = i2400m_dev(i2400m); 1170 struct device *dev = i2400m_dev(i2400m);
1171
1168 unsigned module_type, header_len, major_version, minor_version, 1172 unsigned module_type, header_len, major_version, minor_version,
1169 module_id, module_vendor, date, size; 1173 module_id, module_vendor, date, size;
1170 1174
1171 /* Check hard errors */ 1175 module_type = bcf_hdr->module_type;
1172 result = -EINVAL; 1176 header_len = sizeof(u32) * le32_to_cpu(bcf_hdr->header_len);
1173 if (bcf_size < sizeof(*bcf)) { /* big enough header? */ 1177 major_version = (le32_to_cpu(bcf_hdr->header_version) & 0xffff0000)
1174 dev_err(dev, "firmware %s too short: " 1178 >> 16;
1175 "%zu B vs %zu (at least) expected\n", 1179 minor_version = le32_to_cpu(bcf_hdr->header_version) & 0x0000ffff;
1176 i2400m->fw_name, bcf_size, sizeof(*bcf)); 1180 module_id = le32_to_cpu(bcf_hdr->module_id);
1177 goto error; 1181 module_vendor = le32_to_cpu(bcf_hdr->module_vendor);
1178 } 1182 date = le32_to_cpu(bcf_hdr->date);
1179 1183 size = sizeof(u32) * le32_to_cpu(bcf_hdr->size);
1180 module_type = bcf->module_type; 1184
1181 header_len = sizeof(u32) * le32_to_cpu(bcf->header_len); 1185 d_printf(1, dev, "firmware %s #%d@%08x: BCF header "
1182 major_version = (le32_to_cpu(bcf->header_version) & 0xffff0000) >> 16; 1186 "type:vendor:id 0x%x:%x:%x v%u.%u (%zu/%zu B) built %08x\n",
1183 minor_version = le32_to_cpu(bcf->header_version) & 0x0000ffff; 1187 i2400m->fw_name, index, offset,
1184 module_id = le32_to_cpu(bcf->module_id); 1188 module_type, module_vendor, module_id,
1185 module_vendor = le32_to_cpu(bcf->module_vendor); 1189 major_version, minor_version, header_len, size, date);
1186 date = le32_to_cpu(bcf->date); 1190
1187 size = sizeof(u32) * le32_to_cpu(bcf->size); 1191 /* Hard errors */
1188 1192 if (major_version != 1) {
1189 if (bcf_size != size) { /* annoyingly paranoid */ 1193 dev_err(dev, "firmware %s #%d@%08x: major header version "
1190 dev_err(dev, "firmware %s: bad size, got " 1194 "v%u.%u not supported\n",
1191 "%zu B vs %u expected\n", 1195 i2400m->fw_name, index, offset,
1192 i2400m->fw_name, bcf_size, size); 1196 major_version, minor_version);
1193 goto error; 1197 return -EBADF;
1194 } 1198 }
1195 1199
1196 d_printf(2, dev, "type 0x%x id 0x%x vendor 0x%x; header v%u.%u (%zu B) "
1197 "date %08x (%zu B)\n",
1198 module_type, module_id, module_vendor,
1199 major_version, minor_version, (size_t) header_len,
1200 date, (size_t) size);
1201
1202 if (module_type != 6) { /* built for the right hardware? */ 1200 if (module_type != 6) { /* built for the right hardware? */
1203 dev_err(dev, "bad fw %s: unexpected module type 0x%x; " 1201 dev_err(dev, "firmware %s #%d@%08x: unexpected module "
1204 "aborting\n", i2400m->fw_name, module_type); 1202 "type 0x%x; aborting\n",
1205 goto error; 1203 i2400m->fw_name, index, offset,
1204 module_type);
1205 return -EBADF;
1206 } 1206 }
1207 1207
1208 if (major_version != 1) { 1208 if (module_vendor != 0x8086) {
1209 dev_err(dev, "%s: major header version v%u.%u not supported\n", 1209 dev_err(dev, "firmware %s #%d@%08x: unexpected module "
1210 i2400m->fw_name, major_version, minor_version); 1210 "vendor 0x%x; aborting\n",
1211 goto error; 1211 i2400m->fw_name, index, offset, module_vendor);
1212 return -EBADF;
1212 } 1213 }
1213 1214
1214 /* Check soft-er errors */
1215 result = 0;
1216 if (module_vendor != 0x8086)
1217 dev_err(dev, "bad fw %s? unexpected vendor 0x%04x\n",
1218 i2400m->fw_name, module_vendor);
1219 if (date < 0x20080300) 1215 if (date < 0x20080300)
1220 dev_err(dev, "bad fw %s? build date too old %08x\n", 1216 dev_warn(dev, "firmware %s #%d@%08x: build date %08x "
1221 i2400m->fw_name, date); 1217 "too old; unsupported\n",
1222error: 1218 i2400m->fw_name, index, offset, date);
1219 return 0;
1220}
1221
1222
1223/*
1224 * Run consistency tests on the firmware file and load up headers
1225 *
1226 * Check for the firmware being made for the i2400m device,
1227 * etc...These checks are mostly informative, as the device will make
1228 * them too; but the driver's response is more informative on what
1229 * went wrong.
1230 *
1231 * This will also look at all the headers present on the firmware
1232 * file, and update i2400m->fw_hdrs to point to them.
1233 */
1234static
1235int i2400m_fw_check(struct i2400m *i2400m, const void *bcf, size_t bcf_size)
1236{
1237 int result;
1238 struct device *dev = i2400m_dev(i2400m);
1239 size_t headers = 0;
1240 const struct i2400m_bcf_hdr *bcf_hdr;
1241 const void *itr, *next, *top;
1242 unsigned slots = 0, used_slots = 0;
1243
1244 for (itr = bcf, top = itr + bcf_size;
1245 itr < top;
1246 headers++, itr = next) {
1247 size_t leftover, offset, header_len, size;
1248
1249 leftover = top - itr;
1250 offset = itr - (const void *) bcf;
1251 if (leftover <= sizeof(*bcf_hdr)) {
1252 dev_err(dev, "firmware %s: %zu B left at @%x, "
1253 "not enough for BCF header\n",
1254 i2400m->fw_name, leftover, offset);
1255 break;
1256 }
1257 bcf_hdr = itr;
1258 /* Only the first header is supposed to be followed by
1259 * payload */
1260 header_len = sizeof(u32) * le32_to_cpu(bcf_hdr->header_len);
1261 size = sizeof(u32) * le32_to_cpu(bcf_hdr->size);
1262 if (headers == 0)
1263 next = itr + size;
1264 else
1265 next = itr + header_len;
1266
1267 result = i2400m_fw_hdr_check(i2400m, bcf_hdr, headers, offset);
1268 if (result < 0)
1269 continue;
1270 if (used_slots + 1 >= slots) {
1271 /* +1 -> we need to account for the one we'll
1272 * occupy and at least an extra one for
1273 * always being NULL */
1274 result = i2400m_zrealloc_2x(
1275 (void **) &i2400m->fw_hdrs, &slots,
1276 sizeof(i2400m->fw_hdrs[0]),
1277 GFP_KERNEL);
1278 if (result < 0)
1279 goto error_zrealloc;
1280 }
1281 i2400m->fw_hdrs[used_slots] = bcf_hdr;
1282 used_slots++;
1283 }
1284 if (headers == 0) {
1285 dev_err(dev, "firmware %s: no usable headers found\n",
1286 i2400m->fw_name);
1287 result = -EBADF;
1288 } else
1289 result = 0;
1290error_zrealloc:
1223 return result; 1291 return result;
1224} 1292}
1225 1293
@@ -1359,13 +1427,16 @@ int i2400m_dev_bootstrap(struct i2400m *i2400m, enum i2400m_bri flags)
1359 bcf = (void *) fw->data; 1427 bcf = (void *) fw->data;
1360 i2400m->fw_name = fw_name; 1428 i2400m->fw_name = fw_name;
1361 ret = i2400m_fw_check(i2400m, bcf, fw->size); 1429 ret = i2400m_fw_check(i2400m, bcf, fw->size);
1362 if (ret >= 0) { 1430 if (ret >= 0)
1363 ret = i2400m_fw_dnload(i2400m, bcf, fw->size, flags); 1431 ret = i2400m_fw_dnload(i2400m, bcf, fw->size, flags);
1364 if (ret >= 0) 1432 if (ret < 0)
1365 break; 1433 dev_err(dev, "%s: cannot use: %d, skipping\n",
1366 } else 1434 fw_name, ret);
1367 dev_err(dev, "%s: cannot use, skipping\n", fw_name); 1435 kfree(i2400m->fw_hdrs);
1436 i2400m->fw_hdrs = NULL;
1368 release_firmware(fw); 1437 release_firmware(fw);
1438 if (ret >= 0) /* firmware loaded succesfully */
1439 break;
1369 } 1440 }
1370 d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret); 1441 d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret);
1371 return ret; 1442 return ret;