Bluetooth: btintel: Add Intel devcoredump support
authorAbhishek Pandit-Subedi <abhishekpandit@chromium.org>
Thu, 30 Mar 2023 16:58:26 +0000 (09:58 -0700)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Mon, 24 Apr 2023 04:59:02 +0000 (21:59 -0700)
Intercept debug exception events from the controller and put them into
a devcoredump using hci devcoredump APIs. The debug exception contains
data in a TLV format and it will be parsed in userspace.

Signed-off-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
Signed-off-by: Manish Mandlik <mmandlik@google.com>
Reviewed-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
Reviewed-by: Chethan Tumkur Narayan <chethan.tumkur.narayan@intel.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
drivers/bluetooth/btintel.c
drivers/bluetooth/btintel.h
drivers/bluetooth/btusb.c

index 7a6dc05..d9349ba 100644 (file)
@@ -43,6 +43,12 @@ struct cmd_write_boot_params {
        u8  fw_build_yy;
 } __packed;
 
+static struct {
+       const char *driver_name;
+       u8         hw_variant;
+       u32        fw_build_num;
+} coredump_info;
+
 int btintel_check_bdaddr(struct hci_dev *hdev)
 {
        struct hci_rp_read_bd_addr *bda;
@@ -315,6 +321,9 @@ int btintel_version_info(struct hci_dev *hdev, struct intel_version *ver)
                return -EINVAL;
        }
 
+       coredump_info.hw_variant = ver->hw_variant;
+       coredump_info.fw_build_num = ver->fw_build_num;
+
        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,
@@ -509,6 +518,9 @@ static int btintel_version_info_tlv(struct hci_dev *hdev,
                return -EINVAL;
        }
 
+       coredump_info.hw_variant = INTEL_HW_VARIANT(version->cnvi_bt);
+       coredump_info.fw_build_num = version->build_num;
+
        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);
@@ -1462,6 +1474,59 @@ int btintel_set_quality_report(struct hci_dev *hdev, bool enable)
 }
 EXPORT_SYMBOL_GPL(btintel_set_quality_report);
 
+static void btintel_coredump(struct hci_dev *hdev)
+{
+       struct sk_buff *skb;
+
+       skb = __hci_cmd_sync(hdev, 0xfc4e, 0, NULL, HCI_CMD_TIMEOUT);
+       if (IS_ERR(skb)) {
+               bt_dev_err(hdev, "Coredump failed (%ld)", PTR_ERR(skb));
+               return;
+       }
+
+       kfree_skb(skb);
+}
+
+static void btintel_dmp_hdr(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       char buf[80];
+
+       snprintf(buf, sizeof(buf), "Controller Name: 0x%X\n",
+                coredump_info.hw_variant);
+       skb_put_data(skb, buf, strlen(buf));
+
+       snprintf(buf, sizeof(buf), "Firmware Version: 0x%X\n",
+                coredump_info.fw_build_num);
+       skb_put_data(skb, buf, strlen(buf));
+
+       snprintf(buf, sizeof(buf), "Driver: %s\n", coredump_info.driver_name);
+       skb_put_data(skb, buf, strlen(buf));
+
+       snprintf(buf, sizeof(buf), "Vendor: Intel\n");
+       skb_put_data(skb, buf, strlen(buf));
+}
+
+static int btintel_register_devcoredump_support(struct hci_dev *hdev)
+{
+       struct intel_debug_features features;
+       int err;
+
+       err = btintel_read_debug_features(hdev, &features);
+       if (err) {
+               bt_dev_info(hdev, "Error reading debug features");
+               return err;
+       }
+
+       if (!(features.page1[0] & 0x3f)) {
+               bt_dev_dbg(hdev, "Telemetry exception format not supported");
+               return -EOPNOTSUPP;
+       }
+
+       hci_devcd_register(hdev, btintel_coredump, btintel_dmp_hdr, NULL);
+
+       return err;
+}
+
 static const struct firmware *btintel_legacy_rom_get_fw(struct hci_dev *hdev,
                                               struct intel_version *ver)
 {
@@ -2597,6 +2662,7 @@ static int btintel_setup_combined(struct hci_dev *hdev)
                        btintel_set_msft_opcode(hdev, ver.hw_variant);
 
                        err = btintel_bootloader_setup(hdev, &ver);
+                       btintel_register_devcoredump_support(hdev);
                        break;
                default:
                        bt_dev_err(hdev, "Unsupported Intel hw variant (%u)",
@@ -2670,6 +2736,7 @@ static int btintel_setup_combined(struct hci_dev *hdev)
                btintel_set_msft_opcode(hdev, ver.hw_variant);
 
                err = btintel_bootloader_setup(hdev, &ver);
+               btintel_register_devcoredump_support(hdev);
                break;
        case 0x17:
        case 0x18:
@@ -2692,6 +2759,7 @@ static int btintel_setup_combined(struct hci_dev *hdev)
                                        INTEL_HW_VARIANT(ver_tlv.cnvi_bt));
 
                err = btintel_bootloader_setup_tlv(hdev, &ver_tlv);
+               btintel_register_devcoredump_support(hdev);
                break;
        default:
                bt_dev_err(hdev, "Unsupported Intel hw variant (%u)",
@@ -2741,7 +2809,7 @@ static int btintel_shutdown_combined(struct hci_dev *hdev)
        return 0;
 }
 
-int btintel_configure_setup(struct hci_dev *hdev)
+int btintel_configure_setup(struct hci_dev *hdev, const char *driver_name)
 {
        hdev->manufacturer = 2;
        hdev->setup = btintel_setup_combined;
@@ -2750,6 +2818,8 @@ int btintel_configure_setup(struct hci_dev *hdev)
        hdev->set_diag = btintel_set_diag_combined;
        hdev->set_bdaddr = btintel_set_bdaddr;
 
+       coredump_info.driver_name = driver_name;
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(btintel_configure_setup);
index 8fdb65b..d6a1dc8 100644 (file)
@@ -143,6 +143,13 @@ struct btintel_loc_aware_reg {
        __le32 delta;
 } __packed;
 
+#define INTEL_TLV_TYPE_ID              0x01
+
+#define INTEL_TLV_SYSTEM_EXCEPTION     0x00
+#define INTEL_TLV_FATAL_EXCEPTION      0x01
+#define INTEL_TLV_DEBUG_EXCEPTION      0x02
+#define INTEL_TLV_TEST_EXCEPTION       0xDE
+
 #define INTEL_HW_PLATFORM(cnvx_bt)     ((u8)(((cnvx_bt) & 0x0000ff00) >> 8))
 #define INTEL_HW_VARIANT(cnvx_bt)      ((u8)(((cnvx_bt) & 0x003f0000) >> 16))
 #define INTEL_CNVX_TOP_TYPE(cnvx_top)  ((cnvx_top) & 0x00000fff)
@@ -212,7 +219,7 @@ int btintel_read_boot_params(struct hci_dev *hdev,
                             struct intel_boot_params *params);
 int btintel_download_firmware(struct hci_dev *dev, struct intel_version *ver,
                              const struct firmware *fw, u32 *boot_param);
-int btintel_configure_setup(struct hci_dev *hdev);
+int btintel_configure_setup(struct hci_dev *hdev, const char *driver_name);
 void btintel_bootup(struct hci_dev *hdev, const void *ptr, unsigned int len);
 void btintel_secure_send_result(struct hci_dev *hdev,
                                const void *ptr, unsigned int len);
@@ -293,7 +300,8 @@ static inline int btintel_download_firmware(struct hci_dev *dev,
        return -EOPNOTSUPP;
 }
 
-static inline int btintel_configure_setup(struct hci_dev *hdev)
+static inline int btintel_configure_setup(struct hci_dev *hdev,
+                                         const char *driver_name)
 {
        return -ENODEV;
 }
index 498e131..2303b0a 100644 (file)
@@ -2386,16 +2386,47 @@ static int btusb_recv_bulk_intel(struct btusb_data *data, void *buffer,
        return btusb_recv_bulk(data, buffer, count);
 }
 
+static int btusb_intel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct intel_tlv *tlv = (void *)&skb->data[5];
+
+       /* The first event is always an event type TLV */
+       if (tlv->type != INTEL_TLV_TYPE_ID)
+               goto recv_frame;
+
+       switch (tlv->val[0]) {
+       case INTEL_TLV_SYSTEM_EXCEPTION:
+       case INTEL_TLV_FATAL_EXCEPTION:
+       case INTEL_TLV_DEBUG_EXCEPTION:
+       case INTEL_TLV_TEST_EXCEPTION:
+               /* Generate devcoredump from exception */
+               if (!hci_devcd_init(hdev, skb->len)) {
+                       hci_devcd_append(hdev, skb);
+                       hci_devcd_complete(hdev);
+               } else {
+                       bt_dev_err(hdev, "Failed to generate devcoredump");
+                       kfree_skb(skb);
+               }
+               return 0;
+       default:
+               bt_dev_err(hdev, "Invalid exception type %02X", tlv->val[0]);
+       }
+
+recv_frame:
+       return hci_recv_frame(hdev, skb);
+}
+
 static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) {
-               struct hci_event_hdr *hdr = (void *)skb->data;
+       struct hci_event_hdr *hdr = (void *)skb->data;
+       const char diagnostics_hdr[] = { 0x87, 0x80, 0x03 };
 
-               if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff &&
-                   hdr->plen > 0) {
-                       const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1;
-                       unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1;
+       if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff &&
+           hdr->plen > 0) {
+               const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1;
+               unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1;
 
+               if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) {
                        switch (skb->data[2]) {
                        case 0x02:
                                /* When switching to the operational firmware
@@ -2414,6 +2445,15 @@ static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb)
                                break;
                        }
                }
+
+               /* Handle all diagnostics events separately. May still call
+                * hci_recv_frame.
+                */
+               if (len >= sizeof(diagnostics_hdr) &&
+                   memcmp(&skb->data[2], diagnostics_hdr,
+                          sizeof(diagnostics_hdr)) == 0) {
+                       return btusb_intel_diagnostics(hdev, skb);
+               }
        }
 
        return hci_recv_frame(hdev, skb);
@@ -4018,7 +4058,7 @@ static int btusb_probe(struct usb_interface *intf,
 
        /* Combined Intel Device setup to support multiple setup routine */
        if (id->driver_info & BTUSB_INTEL_COMBINED) {
-               err = btintel_configure_setup(hdev);
+               err = btintel_configure_setup(hdev, btusb_driver.name);
                if (err)
                        goto out_free_dev;