Merge tag 'for-net-next-2021-04-08' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / drivers / bluetooth / btintel.c
index 88ce5f0..e44b699 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;
@@ -208,10 +216,39 @@ void btintel_hw_error(struct hci_dev *hdev, u8 code)
 }
 EXPORT_SYMBOL_GPL(btintel_hw_error);
 
-void btintel_version_info(struct hci_dev *hdev, struct intel_version *ver)
+int btintel_version_info(struct hci_dev *hdev, struct intel_version *ver)
 {
        const char *variant;
 
+       /* The hardware platform number has a fixed value of 0x37 and
+        * for now only accept this single value.
+        */
+       if (ver->hw_platform != 0x37) {
+               bt_dev_err(hdev, "Unsupported Intel hardware platform (%u)",
+                          ver->hw_platform);
+               return -EINVAL;
+       }
+
+       /* Check for supported iBT hardware variants of this firmware
+        * loading method.
+        *
+        * This check has been put in place to ensure correct forward
+        * compatibility options when newer hardware variants come along.
+        */
+       switch (ver->hw_variant) {
+       case 0x0b:      /* SfP */
+       case 0x0c:      /* WsP */
+       case 0x11:      /* JfP */
+       case 0x12:      /* ThP */
+       case 0x13:      /* HrP */
+       case 0x14:      /* CcP */
+               break;
+       default:
+               bt_dev_err(hdev, "Unsupported Intel hardware variant (%u)",
+                          ver->hw_variant);
+               return -EINVAL;
+       }
+
        switch (ver->fw_variant) {
        case 0x06:
                variant = "Bootloader";
@@ -220,13 +257,16 @@ void btintel_version_info(struct hci_dev *hdev, struct intel_version *ver)
                variant = "Firmware";
                break;
        default:
-               return;
+               bt_dev_err(hdev, "Unsupported firmware variant(%02x)", ver->fw_variant);
+               return -EINVAL;
        }
 
        bt_dev_info(hdev, "%s revision %u.%u build %u week %u %u",
                    variant, ver->fw_revision >> 4, ver->fw_revision & 0x0f,
                    ver->fw_build_num, ver->fw_build_ww,
                    2000 + ver->fw_build_yy);
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(btintel_version_info);
 
@@ -364,13 +404,56 @@ int btintel_read_version(struct hci_dev *hdev, struct intel_version *ver)
 }
 EXPORT_SYMBOL_GPL(btintel_read_version);
 
-void btintel_version_info_tlv(struct hci_dev *hdev, struct intel_version_tlv *version)
+int btintel_version_info_tlv(struct hci_dev *hdev, struct intel_version_tlv *version)
 {
        const char *variant;
 
+       /* The hardware platform number has a fixed value of 0x37 and
+        * for now only accept this single value.
+        */
+       if (INTEL_HW_PLATFORM(version->cnvi_bt) != 0x37) {
+               bt_dev_err(hdev, "Unsupported Intel hardware platform (0x%2x)",
+                          INTEL_HW_PLATFORM(version->cnvi_bt));
+               return -EINVAL;
+       }
+
+       /* Check for supported iBT hardware variants of this firmware
+        * loading method.
+        *
+        * This check has been put in place to ensure correct forward
+        * compatibility options when newer hardware variants come along.
+        */
+       switch (INTEL_HW_VARIANT(version->cnvi_bt)) {
+       case 0x17:      /* TyP */
+       case 0x18:      /* Slr */
+       case 0x19:      /* Slr-F */
+               break;
+       default:
+               bt_dev_err(hdev, "Unsupported Intel hardware variant (0x%x)",
+                          INTEL_HW_VARIANT(version->cnvi_bt));
+               return -EINVAL;
+       }
+
        switch (version->img_type) {
        case 0x01:
                variant = "Bootloader";
+               /* It is required that every single firmware fragment is acknowledged
+                * with a command complete event. If the boot parameters indicate
+                * that this bootloader does not send them, then abort the setup.
+                */
+               if (version->limited_cce != 0x00) {
+                       bt_dev_err(hdev, "Unsupported Intel firmware loading method (0x%x)",
+                                  version->limited_cce);
+                       return -EINVAL;
+               }
+
+               /* Secure boot engine type should be either 1 (ECDSA) or 0 (RSA) */
+               if (version->sbe_type > 0x01) {
+                       bt_dev_err(hdev, "Unsupported Intel secure boot engine type (0x%x)",
+                                  version->sbe_type);
+                       return -EINVAL;
+               }
+
                bt_dev_info(hdev, "Device revision is %u", version->dev_rev_id);
                bt_dev_info(hdev, "Secure boot is %s",
                            version->secure_boot ? "enabled" : "disabled");
@@ -389,15 +472,14 @@ void btintel_version_info_tlv(struct hci_dev *hdev, struct intel_version_tlv *ve
                break;
        default:
                bt_dev_err(hdev, "Unsupported image type(%02x)", version->img_type);
-               goto done;
+               return -EINVAL;
        }
 
        bt_dev_info(hdev, "%s timestamp %u.%u buildtype %u build %u", variant,
                    2000 + (version->timestamp >> 8), version->timestamp & 0xff,
                    version->build_type, version->build_num);
 
-done:
-       return;
+       return 0;
 }
 EXPORT_SYMBOL_GPL(btintel_version_info_tlv);
 
@@ -455,12 +537,23 @@ int btintel_read_version_tlv(struct hci_dev *hdev, struct intel_version_tlv *ver
                        version->img_type = tlv->val[0];
                        break;
                case INTEL_TLV_TIME_STAMP:
+                       /* If image type is Operational firmware (0x03), then
+                        * running FW Calendar Week and Year information can
+                        * be extracted from Timestamp information
+                        */
+                       version->min_fw_build_cw = tlv->val[0];
+                       version->min_fw_build_yy = tlv->val[1];
                        version->timestamp = get_unaligned_le16(tlv->val);
                        break;
                case INTEL_TLV_BUILD_TYPE:
                        version->build_type = tlv->val[0];
                        break;
                case INTEL_TLV_BUILD_NUM:
+                       /* If image type is Operational firmware (0x03), then
+                        * running FW build number can be extracted from the
+                        * Build information
+                        */
+                       version->min_fw_build_nn = tlv->val[0];
                        version->build_num = get_unaligned_le32(tlv->val);
                        break;
                case INTEL_TLV_SECURE_BOOT:
@@ -841,7 +934,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,20 +947,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 + 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
@@ -896,28 +975,131 @@ 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 reading firmware file version in bootloader mode */
+               if (ver->fw_variant == 0x06)
+                       break;
+
+               /* 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;
+               }
+       }
+
+       /* The firmware variant determines if the device is in bootloader
+        * mode or is running operational firmware. The value 0x06 identifies
+        * the bootloader and the value 0x23 identifies the operational
+        * firmware.
+        *
+        * If the firmware version has changed that means it needs to be reset
+        * to bootloader when operational so the new firmware can be loaded.
+        */
+       if (ver->fw_variant == 0x23)
+               return -EINVAL;
+
        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 reading firmware file version in bootloader mode */
+       if (ver->img_type != 0x01) {
+               /* 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;
+               }
+       }
+
+       /* The firmware variant determines if the device is in bootloader
+        * mode or is running operational firmware. The value 0x01 identifies
+        * the bootloader and the value 0x03 identifies the operational
+        * firmware.
+        *
+        * If the firmware version has changed that means it needs to be reset
+        * to bootloader when operational so the new firmware can be loaded.
+        */
+       if (ver->img_type == 0x03)
+               return -EINVAL;
+
        /* 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.
@@ -947,7 +1129,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) {
@@ -968,7 +1150,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;
@@ -978,7 +1159,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;