Commit 10607c86 authored by Inaky Perez-Gonzalez's avatar Inaky Perez-Gonzalez

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: default avatarInaky Perez-Gonzalez <inaky@linux.intel.com>
parent bfc44187
...@@ -665,8 +665,8 @@ static int i2400m_download_chunk(struct i2400m *i2400m, const void *chunk, ...@@ -665,8 +665,8 @@ static int i2400m_download_chunk(struct i2400m *i2400m, const void *chunk,
* Download a BCF file's sections to the device * Download a BCF file's sections to the device
* *
* @i2400m: device descriptor * @i2400m: device descriptor
* @bcf: pointer to firmware data (followed by the payloads). Assumed * @bcf: pointer to firmware data (first header followed by the
* verified and consistent. * payloads). Assumed verified and consistent.
* @bcf_len: length (in bytes) of the @bcf buffer. * @bcf_len: length (in bytes) of the @bcf buffer.
* *
* Returns: < 0 errno code on error or the offset to the jump instruction. * 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,11 +756,17 @@ unsigned i2400m_boot_is_signed(struct i2400m *i2400m)
/* /*
* Do the final steps of uploading firmware * Do the final steps of uploading firmware
* *
* @bcf_hdr: BCF header we are actually using
* @bcf: pointer to the firmware image (which matches the first header
* that is followed by the actual payloads).
* @offset: [byte] offset into @bcf for the command we need to send.
*
* Depending on the boot mode (signed vs non-signed), different * Depending on the boot mode (signed vs non-signed), different
* actions need to be taken. * actions need to be taken.
*/ */
static static
int i2400m_dnload_finalize(struct i2400m *i2400m, int i2400m_dnload_finalize(struct i2400m *i2400m,
const struct i2400m_bcf_hdr *bcf_hdr,
const struct i2400m_bcf_hdr *bcf, size_t offset) const struct i2400m_bcf_hdr *bcf, size_t offset)
{ {
int ret = 0; int ret = 0;
...@@ -792,12 +798,13 @@ int i2400m_dnload_finalize(struct i2400m *i2400m, ...@@ -792,12 +798,13 @@ int i2400m_dnload_finalize(struct i2400m *i2400m,
cmd_buf = i2400m->bm_cmd_buf; cmd_buf = i2400m->bm_cmd_buf;
memcpy(&cmd_buf->cmd, cmd, sizeof(*cmd)); memcpy(&cmd_buf->cmd, cmd, sizeof(*cmd));
signature_block_offset = signature_block_offset =
sizeof(*bcf) sizeof(*bcf_hdr)
+ le32_to_cpu(bcf->key_size) * sizeof(u32) + le32_to_cpu(bcf_hdr->key_size) * sizeof(u32)
+ le32_to_cpu(bcf->exponent_size) * sizeof(u32); + le32_to_cpu(bcf_hdr->exponent_size) * sizeof(u32);
signature_block_size = signature_block_size =
le32_to_cpu(bcf->modulus_size) * sizeof(u32); le32_to_cpu(bcf_hdr->modulus_size) * sizeof(u32);
memcpy(cmd_buf->cmd_pl, (void *) bcf + signature_block_offset, memcpy(cmd_buf->cmd_pl,
(void *) bcf_hdr + signature_block_offset,
signature_block_size); signature_block_size);
ret = i2400m_bm_cmd(i2400m, &cmd_buf->cmd, ret = i2400m_bm_cmd(i2400m, &cmd_buf->cmd,
sizeof(cmd_buf->cmd) + signature_block_size, sizeof(cmd_buf->cmd) + signature_block_size,
...@@ -1122,14 +1129,15 @@ int i2400m_dnload_init_signed(struct i2400m *i2400m, ...@@ -1122,14 +1129,15 @@ int i2400m_dnload_init_signed(struct i2400m *i2400m,
* (signed or non-signed). * (signed or non-signed).
*/ */
static static
int i2400m_dnload_init(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf) int i2400m_dnload_init(struct i2400m *i2400m,
const struct i2400m_bcf_hdr *bcf_hdr)
{ {
int result; int result;
struct device *dev = i2400m_dev(i2400m); struct device *dev = i2400m_dev(i2400m);
if (i2400m_boot_is_signed(i2400m)) { if (i2400m_boot_is_signed(i2400m)) {
d_printf(1, dev, "signed boot\n"); d_printf(1, dev, "signed boot\n");
result = i2400m_dnload_init_signed(i2400m, bcf); result = i2400m_dnload_init_signed(i2400m, bcf_hdr);
if (result == -ERESTARTSYS) if (result == -ERESTARTSYS)
return result; return result;
if (result < 0) if (result < 0)
...@@ -1182,15 +1190,15 @@ int i2400m_fw_hdr_check(struct i2400m *i2400m, ...@@ -1182,15 +1190,15 @@ int i2400m_fw_hdr_check(struct i2400m *i2400m,
date = le32_to_cpu(bcf_hdr->date); date = le32_to_cpu(bcf_hdr->date);
size = sizeof(u32) * le32_to_cpu(bcf_hdr->size); size = sizeof(u32) * le32_to_cpu(bcf_hdr->size);
d_printf(1, dev, "firmware %s #%d@%08x: BCF header " d_printf(1, dev, "firmware %s #%zd@%08zx: BCF header "
"type:vendor:id 0x%x:%x:%x v%u.%u (%zu/%zu B) built %08x\n", "type:vendor:id 0x%x:%x:%x v%u.%u (%u/%u B) built %08x\n",
i2400m->fw_name, index, offset, i2400m->fw_name, index, offset,
module_type, module_vendor, module_id, module_type, module_vendor, module_id,
major_version, minor_version, header_len, size, date); major_version, minor_version, header_len, size, date);
/* Hard errors */ /* Hard errors */
if (major_version != 1) { if (major_version != 1) {
dev_err(dev, "firmware %s #%d@%08x: major header version " dev_err(dev, "firmware %s #%zd@%08zx: major header version "
"v%u.%u not supported\n", "v%u.%u not supported\n",
i2400m->fw_name, index, offset, i2400m->fw_name, index, offset,
major_version, minor_version); major_version, minor_version);
...@@ -1198,7 +1206,7 @@ int i2400m_fw_hdr_check(struct i2400m *i2400m, ...@@ -1198,7 +1206,7 @@ int i2400m_fw_hdr_check(struct i2400m *i2400m,
} }
if (module_type != 6) { /* built for the right hardware? */ if (module_type != 6) { /* built for the right hardware? */
dev_err(dev, "firmware %s #%d@%08x: unexpected module " dev_err(dev, "firmware %s #%zd@%08zx: unexpected module "
"type 0x%x; aborting\n", "type 0x%x; aborting\n",
i2400m->fw_name, index, offset, i2400m->fw_name, index, offset,
module_type); module_type);
...@@ -1206,14 +1214,14 @@ int i2400m_fw_hdr_check(struct i2400m *i2400m, ...@@ -1206,14 +1214,14 @@ int i2400m_fw_hdr_check(struct i2400m *i2400m,
} }
if (module_vendor != 0x8086) { if (module_vendor != 0x8086) {
dev_err(dev, "firmware %s #%d@%08x: unexpected module " dev_err(dev, "firmware %s #%zd@%08zx: unexpected module "
"vendor 0x%x; aborting\n", "vendor 0x%x; aborting\n",
i2400m->fw_name, index, offset, module_vendor); i2400m->fw_name, index, offset, module_vendor);
return -EBADF; return -EBADF;
} }
if (date < 0x20080300) if (date < 0x20080300)
dev_warn(dev, "firmware %s #%d@%08x: build date %08x " dev_warn(dev, "firmware %s #%zd@%08zx: build date %08x "
"too old; unsupported\n", "too old; unsupported\n",
i2400m->fw_name, index, offset, date); i2400m->fw_name, index, offset, date);
return 0; return 0;
...@@ -1239,7 +1247,7 @@ int i2400m_fw_check(struct i2400m *i2400m, const void *bcf, size_t bcf_size) ...@@ -1239,7 +1247,7 @@ int i2400m_fw_check(struct i2400m *i2400m, const void *bcf, size_t bcf_size)
size_t headers = 0; size_t headers = 0;
const struct i2400m_bcf_hdr *bcf_hdr; const struct i2400m_bcf_hdr *bcf_hdr;
const void *itr, *next, *top; const void *itr, *next, *top;
unsigned slots = 0, used_slots = 0; size_t slots = 0, used_slots = 0;
for (itr = bcf, top = itr + bcf_size; for (itr = bcf, top = itr + bcf_size;
itr < top; itr < top;
...@@ -1249,7 +1257,7 @@ int i2400m_fw_check(struct i2400m *i2400m, const void *bcf, size_t bcf_size) ...@@ -1249,7 +1257,7 @@ int i2400m_fw_check(struct i2400m *i2400m, const void *bcf, size_t bcf_size)
leftover = top - itr; leftover = top - itr;
offset = itr - (const void *) bcf; offset = itr - (const void *) bcf;
if (leftover <= sizeof(*bcf_hdr)) { if (leftover <= sizeof(*bcf_hdr)) {
dev_err(dev, "firmware %s: %zu B left at @%x, " dev_err(dev, "firmware %s: %zu B left at @%zx, "
"not enough for BCF header\n", "not enough for BCF header\n",
i2400m->fw_name, leftover, offset); i2400m->fw_name, leftover, offset);
break; break;
...@@ -1292,6 +1300,60 @@ error_zrealloc: ...@@ -1292,6 +1300,60 @@ error_zrealloc:
} }
/*
* Match a barker to a BCF header module ID
*
* The device sends a barker which tells the firmware loader which
* header in the BCF file has to be used. This does the matching.
*/
static
unsigned i2400m_bcf_hdr_match(struct i2400m *i2400m,
const struct i2400m_bcf_hdr *bcf_hdr)
{
u32 barker = le32_to_cpu(i2400m->barker->data[0])
& 0x7fffffff;
u32 module_id = le32_to_cpu(bcf_hdr->module_id)
& 0x7fffffff; /* high bit used for something else */
/* special case for 5x50 */
if (barker == I2400M_SBOOT_BARKER && module_id == 0)
return 1;
if (module_id == barker)
return 1;
return 0;
}
static
const struct i2400m_bcf_hdr *i2400m_bcf_hdr_find(struct i2400m *i2400m)
{
struct device *dev = i2400m_dev(i2400m);
const struct i2400m_bcf_hdr **bcf_itr, *bcf_hdr;
unsigned i = 0;
u32 barker = le32_to_cpu(i2400m->barker->data[0]);
d_printf(2, dev, "finding BCF header for barker %08x\n", barker);
if (barker == I2400M_NBOOT_BARKER) {
bcf_hdr = i2400m->fw_hdrs[0];
d_printf(1, dev, "using BCF header #%u/%08x for non-signed "
"barker\n", 0, le32_to_cpu(bcf_hdr->module_id));
return bcf_hdr;
}
for (bcf_itr = i2400m->fw_hdrs; *bcf_itr != NULL; bcf_itr++, i++) {
bcf_hdr = *bcf_itr;
if (i2400m_bcf_hdr_match(i2400m, bcf_hdr)) {
d_printf(1, dev, "hit on BCF hdr #%u/%08x\n",
i, le32_to_cpu(bcf_hdr->module_id));
return bcf_hdr;
} else
d_printf(1, dev, "miss on BCF hdr #%u/%08x\n",
i, le32_to_cpu(bcf_hdr->module_id));
}
dev_err(dev, "cannot find a matching BCF header for barker %08x\n",
barker);
return NULL;
}
/* /*
* Download the firmware to the device * Download the firmware to the device
* *
...@@ -1313,6 +1375,7 @@ int i2400m_fw_dnload(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf, ...@@ -1313,6 +1375,7 @@ int i2400m_fw_dnload(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf,
int ret = 0; int ret = 0;
struct device *dev = i2400m_dev(i2400m); struct device *dev = i2400m_dev(i2400m);
int count = i2400m->bus_bm_retries; int count = i2400m->bus_bm_retries;
const struct i2400m_bcf_hdr *bcf_hdr;
d_fnstart(5, dev, "(i2400m %p bcf %p size %zu)\n", d_fnstart(5, dev, "(i2400m %p bcf %p size %zu)\n",
i2400m, bcf, bcf_size); i2400m, bcf, bcf_size);
...@@ -1337,8 +1400,17 @@ hw_reboot: ...@@ -1337,8 +1400,17 @@ hw_reboot:
* Initialize the download, push the bytes to the device and * Initialize the download, push the bytes to the device and
* then jump to the new firmware. Note @ret is passed with the * then jump to the new firmware. Note @ret is passed with the
* offset of the jump instruction to _dnload_finalize() * offset of the jump instruction to _dnload_finalize()
*
* Note we need to use the BCF header in the firmware image
* that matches the barker that the device sent when it
* rebooted, so it has to be passed along.
*/ */
ret = i2400m_dnload_init(i2400m, bcf); /* Init device's dnload */ ret = -EBADF;
bcf_hdr = i2400m_bcf_hdr_find(i2400m);
if (bcf_hdr == NULL)
goto error_bcf_hdr_find;
ret = i2400m_dnload_init(i2400m, bcf_hdr);
if (ret == -ERESTARTSYS) if (ret == -ERESTARTSYS)
goto error_dev_rebooted; goto error_dev_rebooted;
if (ret < 0) if (ret < 0)
...@@ -1353,7 +1425,7 @@ hw_reboot: ...@@ -1353,7 +1425,7 @@ hw_reboot:
goto error_dnload_bcf; goto error_dnload_bcf;
} }
ret = i2400m_dnload_finalize(i2400m, bcf, ret); ret = i2400m_dnload_finalize(i2400m, bcf_hdr, bcf, ret);
if (ret == -ERESTARTSYS) if (ret == -ERESTARTSYS)
goto error_dev_rebooted; goto error_dev_rebooted;
if (ret < 0) { if (ret < 0) {
...@@ -1370,6 +1442,7 @@ hw_reboot: ...@@ -1370,6 +1442,7 @@ hw_reboot:
error_dnload_finalize: error_dnload_finalize:
error_dnload_bcf: error_dnload_bcf:
error_dnload_init: error_dnload_init:
error_bcf_hdr_find:
error_bootrom_init: error_bootrom_init:
error_too_many_reboots: error_too_many_reboots:
d_fnend(5, dev, "(i2400m %p bcf %p size %zu) = %d\n", d_fnend(5, dev, "(i2400m %p bcf %p size %zu) = %d\n",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment