Bluetooth: btintel: Check firmware version before download
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Tue, 23 Mar 2021 18:58:56 +0000 (11:58 -0700)
committerMarcel Holtmann <marcel@holtmann.org>
Thu, 25 Mar 2021 15:09:35 +0000 (16:09 +0100)
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>
drivers/bluetooth/btintel.c
drivers/bluetooth/btintel.h
drivers/bluetooth/btusb.c
drivers/bluetooth/hci_intel.c

index fa97324..3ff698a 100644 (file)
 #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;
@@ -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;
@@ -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
@@ -897,28 +890,101 @@ done:
        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.
@@ -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) {
@@ -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;
@@ -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;
index 6511b09..51f1f2c 100644 (file)
@@ -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);
index 0d355bb..de220ab 100644 (file)
@@ -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.
                 */
@@ -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.
                 */
index b20a40f..7249b91 100644 (file)
@@ -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;
 
@@ -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 */