Skip to content

Commit

Permalink
Bluetooth: btintel: Check firmware version before download
Browse files Browse the repository at this point in the history
This checks the firmware build number, week and year against the
repective loaded version. If details are a match, skip the download
process.

Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Tested-by: Tedd Ho-Jeong An <tedd.an@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
  • Loading branch information
Luiz Augusto von Dentz authored and Marcel Holtmann committed Mar 25, 2021
1 parent 0f90d32 commit ac05654
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 27 deletions.
106 changes: 85 additions & 21 deletions drivers/bluetooth/btintel.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@
#define ECDSA_OFFSET 644
#define ECDSA_HEADER_LEN 320

#define CMD_WRITE_BOOT_PARAMS 0xfc0e
struct cmd_write_boot_params {
u32 boot_addr;
u8 fw_build_num;
u8 fw_build_ww;
u8 fw_build_yy;
} __packed;

int btintel_check_bdaddr(struct hci_dev *hdev)
{
struct hci_rp_read_bd_addr *bda;
Expand Down Expand Up @@ -841,7 +849,7 @@ static int btintel_sfi_ecdsa_header_secure_send(struct hci_dev *hdev,

static int btintel_download_firmware_payload(struct hci_dev *hdev,
const struct firmware *fw,
u32 *boot_param, size_t offset)
size_t offset)
{
int err;
const u8 *fw_ptr;
Expand All @@ -854,21 +862,6 @@ static int btintel_download_firmware_payload(struct hci_dev *hdev,
while (fw_ptr - fw->data < fw->size) {
struct hci_command_hdr *cmd = (void *)(fw_ptr + frag_len);

/* Each SKU has a different reset parameter to use in the
* HCI_Intel_Reset command and it is embedded in the firmware
* data. So, instead of using static value per SKU, check
* the firmware data and save it for later use.
*/
if (le16_to_cpu(cmd->opcode) == 0xfc0e) {
/* The boot parameter is the first 32-bit value
* and rest of 3 octets are reserved.
*/
*boot_param = get_unaligned_le32(fw_ptr + frag_len +
sizeof(*cmd));

bt_dev_dbg(hdev, "boot_param=0x%x", *boot_param);
}

frag_len += sizeof(*cmd) + cmd->plen;

/* The parameter length of the secure send command requires
Expand Down Expand Up @@ -897,28 +890,101 @@ static int btintel_download_firmware_payload(struct hci_dev *hdev,
return err;
}

static bool btintel_firmware_version(struct hci_dev *hdev,
u8 num, u8 ww, u8 yy,
const struct firmware *fw,
u32 *boot_addr)
{
const u8 *fw_ptr;

fw_ptr = fw->data;

while (fw_ptr - fw->data < fw->size) {
struct hci_command_hdr *cmd = (void *)(fw_ptr);

/* Each SKU has a different reset parameter to use in the
* HCI_Intel_Reset command and it is embedded in the firmware
* data. So, instead of using static value per SKU, check
* the firmware data and save it for later use.
*/
if (le16_to_cpu(cmd->opcode) == CMD_WRITE_BOOT_PARAMS) {
struct cmd_write_boot_params *params;

params = (void *)(fw_ptr + sizeof(*cmd));

bt_dev_info(hdev, "Boot Address: 0x%x",
le32_to_cpu(params->boot_addr));

bt_dev_info(hdev, "Firmware Version: %u-%u.%u",
params->fw_build_num, params->fw_build_ww,
params->fw_build_yy);

return (num == params->fw_build_num &&
ww == params->fw_build_ww &&
yy == params->fw_build_yy);
}

fw_ptr += sizeof(*cmd) + cmd->plen;
}

return false;
}

int btintel_download_firmware(struct hci_dev *hdev,
struct intel_version *ver,
const struct firmware *fw,
u32 *boot_param)
{
int err;

/* SfP and WsP don't seem to update the firmware version on file
* so version checking is currently not possible.
*/
switch (ver->hw_variant) {
case 0x0b: /* SfP */
case 0x0c: /* WsP */
/* Skip version checking */
break;
default:
/* Skip download if firmware has the same version */
if (btintel_firmware_version(hdev, ver->fw_build_num,
ver->fw_build_ww, ver->fw_build_yy,
fw, boot_param)) {
bt_dev_info(hdev, "Firmware already loaded");
/* Return -EALREADY to indicate that the firmware has
* already been loaded.
*/
return -EALREADY;
}
}

err = btintel_sfi_rsa_header_secure_send(hdev, fw);
if (err)
return err;

return btintel_download_firmware_payload(hdev, fw, boot_param,
RSA_HEADER_LEN);
return btintel_download_firmware_payload(hdev, fw, RSA_HEADER_LEN);
}
EXPORT_SYMBOL_GPL(btintel_download_firmware);

int btintel_download_firmware_newgen(struct hci_dev *hdev,
struct intel_version_tlv *ver,
const struct firmware *fw, u32 *boot_param,
u8 hw_variant, u8 sbe_type)
{
int err;
u32 css_header_ver;

/* Skip download if firmware has the same version */
if (btintel_firmware_version(hdev, ver->min_fw_build_nn,
ver->min_fw_build_cw, ver->min_fw_build_yy,
fw, boot_param)) {
bt_dev_info(hdev, "Firmware already loaded");
/* Return -EALREADY to indicate that firmware has already been
* loaded.
*/
return -EALREADY;
}

/* iBT hardware variants 0x0b, 0x0c, 0x11, 0x12, 0x13, 0x14 support
* only RSA secure boot engine. Hence, the corresponding sfi file will
* have RSA header of 644 bytes followed by Command Buffer.
Expand Down Expand Up @@ -948,7 +1014,7 @@ int btintel_download_firmware_newgen(struct hci_dev *hdev,
if (err)
return err;

err = btintel_download_firmware_payload(hdev, fw, boot_param, RSA_HEADER_LEN);
err = btintel_download_firmware_payload(hdev, fw, RSA_HEADER_LEN);
if (err)
return err;
} else if (hw_variant >= 0x17) {
Expand All @@ -969,7 +1035,6 @@ int btintel_download_firmware_newgen(struct hci_dev *hdev,
return err;

err = btintel_download_firmware_payload(hdev, fw,
boot_param,
RSA_HEADER_LEN + ECDSA_HEADER_LEN);
if (err)
return err;
Expand All @@ -979,7 +1044,6 @@ int btintel_download_firmware_newgen(struct hci_dev *hdev,
return err;

err = btintel_download_firmware_payload(hdev, fw,
boot_param,
RSA_HEADER_LEN + ECDSA_HEADER_LEN);
if (err)
return err;
Expand Down
5 changes: 3 additions & 2 deletions drivers/bluetooth/btintel.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,10 @@ struct regmap *btintel_regmap_init(struct hci_dev *hdev, u16 opcode_read,
int btintel_send_intel_reset(struct hci_dev *hdev, u32 boot_param);
int btintel_read_boot_params(struct hci_dev *hdev,
struct intel_boot_params *params);
int btintel_download_firmware(struct hci_dev *dev, const struct firmware *fw,
u32 *boot_param);
int btintel_download_firmware(struct hci_dev *dev, struct intel_version *ver,
const struct firmware *fw, u32 *boot_param);
int btintel_download_firmware_newgen(struct hci_dev *hdev,
struct intel_version_tlv *ver,
const struct firmware *fw,
u32 *boot_param, u8 hw_variant,
u8 sbe_type);
Expand Down
18 changes: 16 additions & 2 deletions drivers/bluetooth/btusb.c
Original file line number Diff line number Diff line change
Expand Up @@ -2557,10 +2557,17 @@ static int btusb_intel_download_firmware_newgen(struct hci_dev *hdev,
set_bit(BTUSB_DOWNLOADING, &data->flags);

/* Start firmware downloading and get boot parameter */
err = btintel_download_firmware_newgen(hdev, fw, boot_param,
err = btintel_download_firmware_newgen(hdev, ver, fw, boot_param,
INTEL_HW_VARIANT(ver->cnvi_bt),
ver->sbe_type);
if (err < 0) {
if (err == -EALREADY) {
/* Firmware has already been loaded */
set_bit(BTUSB_FIRMWARE_LOADED, &data->flags);
err = 0;
goto done;
}

/* When FW download fails, send Intel Reset to retry
* FW download.
*/
Expand Down Expand Up @@ -2752,8 +2759,15 @@ static int btusb_intel_download_firmware(struct hci_dev *hdev,
set_bit(BTUSB_DOWNLOADING, &data->flags);

/* Start firmware downloading and get boot parameter */
err = btintel_download_firmware(hdev, fw, boot_param);
err = btintel_download_firmware(hdev, ver, fw, boot_param);
if (err < 0) {
if (err == -EALREADY) {
/* Firmware has already been loaded */
set_bit(BTUSB_FIRMWARE_LOADED, &data->flags);
err = 0;
goto done;
}

/* When FW download fails, send Intel Reset to retry
* FW download.
*/
Expand Down
7 changes: 5 additions & 2 deletions drivers/bluetooth/hci_intel.c
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,7 @@ static int intel_setup(struct hci_uart *hu)
set_bit(STATE_DOWNLOADING, &intel->flags);

/* Start firmware downloading and get boot parameter */
err = btintel_download_firmware(hdev, fw, &boot_param);
err = btintel_download_firmware(hdev, &ver, fw, &boot_param);
if (err < 0)
goto done;

Expand Down Expand Up @@ -784,7 +784,10 @@ static int intel_setup(struct hci_uart *hu)
done:
release_firmware(fw);

if (err < 0)
/* Check if there was an error and if is not -EALREADY which means the
* firmware has already been loaded.
*/
if (err < 0 && err != -EALREADY)
return err;

/* We need to restore the default speed before Intel reset */
Expand Down

0 comments on commit ac05654

Please sign in to comment.