Merge tag 'armsoc-defconfig' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
[linux-2.6-microblaze.git] / drivers / bluetooth / btintel.c
index 5fa5be3..88ce5f0 100644 (file)
 
 #define VERSION "0.1"
 
-#define BDADDR_INTEL (&(bdaddr_t) {{0x00, 0x8b, 0x9e, 0x19, 0x03, 0x00}})
+#define BDADDR_INTEL           (&(bdaddr_t){{0x00, 0x8b, 0x9e, 0x19, 0x03, 0x00}})
+#define RSA_HEADER_LEN         644
+#define CSS_HEADER_OFFSET      8
+#define ECDSA_OFFSET           644
+#define ECDSA_HEADER_LEN       320
 
 int btintel_check_bdaddr(struct hci_dev *hdev)
 {
@@ -360,6 +364,144 @@ 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)
+{
+       const char *variant;
+
+       switch (version->img_type) {
+       case 0x01:
+               variant = "Bootloader";
+               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");
+               bt_dev_info(hdev, "OTP lock is %s",
+                           version->otp_lock ? "enabled" : "disabled");
+               bt_dev_info(hdev, "API lock is %s",
+                           version->api_lock ? "enabled" : "disabled");
+               bt_dev_info(hdev, "Debug lock is %s",
+                           version->debug_lock ? "enabled" : "disabled");
+               bt_dev_info(hdev, "Minimum firmware build %u week %u %u",
+                           version->min_fw_build_nn, version->min_fw_build_cw,
+                           2000 + version->min_fw_build_yy);
+               break;
+       case 0x03:
+               variant = "Firmware";
+               break;
+       default:
+               bt_dev_err(hdev, "Unsupported image type(%02x)", version->img_type);
+               goto done;
+       }
+
+       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;
+}
+EXPORT_SYMBOL_GPL(btintel_version_info_tlv);
+
+int btintel_read_version_tlv(struct hci_dev *hdev, struct intel_version_tlv *version)
+{
+       struct sk_buff *skb;
+       const u8 param[1] = { 0xFF };
+
+       if (!version)
+               return -EINVAL;
+
+       skb = __hci_cmd_sync(hdev, 0xfc05, 1, param, HCI_CMD_TIMEOUT);
+       if (IS_ERR(skb)) {
+               bt_dev_err(hdev, "Reading Intel version information failed (%ld)",
+                          PTR_ERR(skb));
+               return PTR_ERR(skb);
+       }
+
+       if (skb->data[0]) {
+               bt_dev_err(hdev, "Intel Read Version command failed (%02x)",
+                          skb->data[0]);
+               kfree_skb(skb);
+               return -EIO;
+       }
+
+       /* Consume Command Complete Status field */
+       skb_pull(skb, 1);
+
+       /* Event parameters contatin multiple TLVs. Read each of them
+        * and only keep the required data. Also, it use existing legacy
+        * version field like hw_platform, hw_variant, and fw_variant
+        * to keep the existing setup flow
+        */
+       while (skb->len) {
+               struct intel_tlv *tlv;
+
+               tlv = (struct intel_tlv *)skb->data;
+               switch (tlv->type) {
+               case INTEL_TLV_CNVI_TOP:
+                       version->cnvi_top = get_unaligned_le32(tlv->val);
+                       break;
+               case INTEL_TLV_CNVR_TOP:
+                       version->cnvr_top = get_unaligned_le32(tlv->val);
+                       break;
+               case INTEL_TLV_CNVI_BT:
+                       version->cnvi_bt = get_unaligned_le32(tlv->val);
+                       break;
+               case INTEL_TLV_CNVR_BT:
+                       version->cnvr_bt = get_unaligned_le32(tlv->val);
+                       break;
+               case INTEL_TLV_DEV_REV_ID:
+                       version->dev_rev_id = get_unaligned_le16(tlv->val);
+                       break;
+               case INTEL_TLV_IMAGE_TYPE:
+                       version->img_type = tlv->val[0];
+                       break;
+               case INTEL_TLV_TIME_STAMP:
+                       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:
+                       version->build_num = get_unaligned_le32(tlv->val);
+                       break;
+               case INTEL_TLV_SECURE_BOOT:
+                       version->secure_boot = tlv->val[0];
+                       break;
+               case INTEL_TLV_OTP_LOCK:
+                       version->otp_lock = tlv->val[0];
+                       break;
+               case INTEL_TLV_API_LOCK:
+                       version->api_lock = tlv->val[0];
+                       break;
+               case INTEL_TLV_DEBUG_LOCK:
+                       version->debug_lock = tlv->val[0];
+                       break;
+               case INTEL_TLV_MIN_FW:
+                       version->min_fw_build_nn = tlv->val[0];
+                       version->min_fw_build_cw = tlv->val[1];
+                       version->min_fw_build_yy = tlv->val[2];
+                       break;
+               case INTEL_TLV_LIMITED_CCE:
+                       version->limited_cce = tlv->val[0];
+                       break;
+               case INTEL_TLV_SBE_TYPE:
+                       version->sbe_type = tlv->val[0];
+                       break;
+               case INTEL_TLV_OTP_BDADDR:
+                       memcpy(&version->otp_bd_addr, tlv->val, tlv->len);
+                       break;
+               default:
+                       /* Ignore rest of information */
+                       break;
+               }
+               /* consume the current tlv and move to next*/
+               skb_pull(skb, tlv->len + sizeof(*tlv));
+       }
+
+       kfree_skb(skb);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(btintel_read_version_tlv);
+
 /* ------- REGMAP IBT SUPPORT ------- */
 
 #define IBT_REG_MODE_8BIT  0x00
@@ -626,12 +768,10 @@ int btintel_read_boot_params(struct hci_dev *hdev,
 }
 EXPORT_SYMBOL_GPL(btintel_read_boot_params);
 
-int btintel_download_firmware(struct hci_dev *hdev, const struct firmware *fw,
-                             u32 *boot_param)
+static int btintel_sfi_rsa_header_secure_send(struct hci_dev *hdev,
+                                             const struct firmware *fw)
 {
        int err;
-       const u8 *fw_ptr;
-       u32 frag_len;
 
        /* Start the firmware download transaction with the Init fragment
         * represented by the 128 bytes of CSS header.
@@ -660,8 +800,56 @@ int btintel_download_firmware(struct hci_dev *hdev, const struct firmware *fw,
                goto done;
        }
 
-       fw_ptr = fw->data + 644;
+done:
+       return err;
+}
+
+static int btintel_sfi_ecdsa_header_secure_send(struct hci_dev *hdev,
+                                               const struct firmware *fw)
+{
+       int err;
+
+       /* Start the firmware download transaction with the Init fragment
+        * represented by the 128 bytes of CSS header.
+        */
+       err = btintel_secure_send(hdev, 0x00, 128, fw->data + 644);
+       if (err < 0) {
+               bt_dev_err(hdev, "Failed to send firmware header (%d)", err);
+               return err;
+       }
+
+       /* Send the 96 bytes of public key information from the firmware
+        * as the PKey fragment.
+        */
+       err = btintel_secure_send(hdev, 0x03, 96, fw->data + 644 + 128);
+       if (err < 0) {
+               bt_dev_err(hdev, "Failed to send firmware pkey (%d)", err);
+               return err;
+       }
+
+       /* Send the 96 bytes of signature information from the firmware
+        * as the Sign fragment
+        */
+       err = btintel_secure_send(hdev, 0x02, 96, fw->data + 644 + 224);
+       if (err < 0) {
+               bt_dev_err(hdev, "Failed to send firmware signature (%d)",
+                          err);
+               return err;
+       }
+       return 0;
+}
+
+static int btintel_download_firmware_payload(struct hci_dev *hdev,
+                                            const struct firmware *fw,
+                                            u32 *boot_param, size_t offset)
+{
+       int err;
+       const u8 *fw_ptr;
+       u32 frag_len;
+
+       fw_ptr = fw->data + offset;
        frag_len = 0;
+       err = -EINVAL;
 
        while (fw_ptr - fw->data < fw->size) {
                struct hci_command_hdr *cmd = (void *)(fw_ptr + frag_len);
@@ -707,8 +895,99 @@ int btintel_download_firmware(struct hci_dev *hdev, const struct firmware *fw,
 done:
        return err;
 }
+
+int btintel_download_firmware(struct hci_dev *hdev,
+                             const struct firmware *fw,
+                             u32 *boot_param)
+{
+       int err;
+
+       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);
+}
 EXPORT_SYMBOL_GPL(btintel_download_firmware);
 
+int btintel_download_firmware_newgen(struct hci_dev *hdev,
+                                    const struct firmware *fw, u32 *boot_param,
+                                    u8 hw_variant, u8 sbe_type)
+{
+       int err;
+       u32 css_header_ver;
+
+       /* 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.
+        *
+        * iBT hardware variants 0x17, 0x18 onwards support both RSA and ECDSA
+        * secure boot engine. As a result, the corresponding sfi file will
+        * have RSA header of 644, ECDSA header of 320 bytes followed by
+        * Command Buffer.
+        *
+        * CSS Header byte positions 0x08 to 0x0B represent the CSS Header
+        * version: RSA(0x00010000) , ECDSA (0x00020000)
+        */
+       css_header_ver = get_unaligned_le32(fw->data + CSS_HEADER_OFFSET);
+       if (css_header_ver != 0x00010000) {
+               bt_dev_err(hdev, "Invalid CSS Header version");
+               return -EINVAL;
+       }
+
+       if (hw_variant <= 0x14) {
+               if (sbe_type != 0x00) {
+                       bt_dev_err(hdev, "Invalid SBE type for hardware variant (%d)",
+                                  hw_variant);
+                       return -EINVAL;
+               }
+
+               err = btintel_sfi_rsa_header_secure_send(hdev, fw);
+               if (err)
+                       return err;
+
+               err = btintel_download_firmware_payload(hdev, fw, boot_param, RSA_HEADER_LEN);
+               if (err)
+                       return err;
+       } else if (hw_variant >= 0x17) {
+               /* Check if CSS header for ECDSA follows the RSA header */
+               if (fw->data[ECDSA_OFFSET] != 0x06)
+                       return -EINVAL;
+
+               /* Check if the CSS Header version is ECDSA(0x00020000) */
+               css_header_ver = get_unaligned_le32(fw->data + ECDSA_OFFSET + CSS_HEADER_OFFSET);
+               if (css_header_ver != 0x00020000) {
+                       bt_dev_err(hdev, "Invalid CSS Header version");
+                       return -EINVAL;
+               }
+
+               if (sbe_type == 0x00) {
+                       err = btintel_sfi_rsa_header_secure_send(hdev, fw);
+                       if (err)
+                               return err;
+
+                       err = btintel_download_firmware_payload(hdev, fw,
+                                                               boot_param,
+                                                               RSA_HEADER_LEN + ECDSA_HEADER_LEN);
+                       if (err)
+                               return err;
+               } else if (sbe_type == 0x01) {
+                       err = btintel_sfi_ecdsa_header_secure_send(hdev, fw);
+                       if (err)
+                               return err;
+
+                       err = btintel_download_firmware_payload(hdev, fw,
+                                                               boot_param,
+                                                               RSA_HEADER_LEN + ECDSA_HEADER_LEN);
+                       if (err)
+                               return err;
+               }
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(btintel_download_firmware_newgen);
+
 void btintel_reset_to_bootloader(struct hci_dev *hdev)
 {
        struct intel_reset params;