| From 00266ef1413217e68b5b662dc5f778636536a5c4 Mon Sep 17 00:00:00 2001 |
| From: Abhishek Pandit-Subedi <abhishekpandit@chromium.org> |
| Date: Wed, 28 Oct 2020 21:08:27 -0700 |
| Subject: [PATCH] BACKPORT: FROMLIST: Bluetooth: btintel: Add Intel devcoredump |
| support |
| |
| 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> |
| (am from https://patchwork.kernel.org/patch/12940743) |
| |
| BACKPORT Notes: Fix btusb_recv_event_intel() and add btintel_coredump() |
| implementation. |
| |
| BUG=b:154867197 |
| TEST=echo 1 > /sys/class/bluetooth/hci0/device/coredump and verify that |
| the coredump file is generated in /var/spool/crash |
| |
| Change-Id: I61857ea92be43418b8ce16839f79a4704cc89fa6 |
| Signed-off-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org> |
| Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/3760829 |
| Tested-by: Manish Mandlik <mmandlik@chromium.org> |
| Reviewed-by: Abhishek Pandit-Subedi <abhishekpandit@google.com> |
| Commit-Queue: Manish Mandlik <mmandlik@chromium.org> |
| --- |
| drivers/bluetooth/btintel.c | 79 ++++++++++++++++++++++++++++++++++++- |
| drivers/bluetooth/btintel.h | 11 +++++- |
| drivers/bluetooth/btusb.c | 51 ++++++++++++++++++++---- |
| 3 files changed, 131 insertions(+), 10 deletions(-) |
| |
| diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c |
| index d436679b026c37a610e86ec8b0ba30a292e007f4..8e8b6a983d2d1e794b93b709326af6ba6cf7ebdc 100644 |
| --- a/drivers/bluetooth/btintel.c |
| +++ b/drivers/bluetooth/btintel.c |
| @@ -36,6 +36,13 @@ struct cmd_write_boot_params { |
| u8 fw_build_yy; |
| } __packed; |
| |
| +#define DRIVER_NAME_LEN 16 |
| +static struct { |
| + char driver_name[DRIVER_NAME_LEN]; |
| + u8 hw_variant; |
| + u32 fw_build_num; |
| +} coredump_info; |
| + |
| int btintel_check_bdaddr(struct hci_dev *hdev) |
| { |
| struct hci_rp_read_bd_addr *bda; |
| @@ -308,6 +315,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, |
| @@ -502,6 +512,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); |
| @@ -1492,6 +1505,65 @@ bool btintel_pull_quality_report_data(struct sk_buff *skb) |
| } |
| EXPORT_SYMBOL_GPL(btintel_pull_quality_report_data); |
| |
| +static void btintel_coredump(struct hci_dev *hdev) |
| +{ |
| + static const u8 param[] = { 0x00, 0x00 }; |
| + struct sk_buff *skb; |
| + |
| + skb = __hci_cmd_sync(hdev, 0xfc4d, 2, param, HCI_CMD_TIMEOUT); |
| + if (IS_ERR(skb)) |
| + bt_dev_err(hdev, "Coredump failed (%ld)", PTR_ERR(skb)); |
| + kfree_skb(skb); |
| +} |
| + |
| +static int btintel_dmp_hdr(struct hci_dev *hdev, char *buf, size_t size) |
| +{ |
| + char *ptr = buf; |
| + size_t rem = size; |
| + size_t read = 0; |
| + |
| + read = snprintf(ptr, rem, "Controller Name: 0x%X\n", |
| + coredump_info.hw_variant); |
| + rem -= read; |
| + ptr += read; |
| + |
| + read = snprintf(ptr, rem, "Firmware Version: 0x%X\n", |
| + coredump_info.fw_build_num); |
| + rem -= read; |
| + ptr += read; |
| + |
| + read = snprintf(ptr, rem, "Driver: %s\n", coredump_info.driver_name); |
| + rem -= read; |
| + ptr += read; |
| + |
| + read = snprintf(ptr, rem, "Vendor: Intel\n"); |
| + rem -= read; |
| + ptr += read; |
| + |
| + return size - rem; |
| +} |
| + |
| +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_info(hdev, "Telemetry exception format not supported"); |
| + return -EOPNOTSUPP; |
| + } |
| + |
| + hci_devcoredump_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) |
| { |
| @@ -2623,6 +2695,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)", |
| @@ -2696,6 +2769,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: |
| @@ -2719,6 +2793,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)", |
| @@ -2768,7 +2843,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; |
| @@ -2777,6 +2852,8 @@ int btintel_configure_setup(struct hci_dev *hdev) |
| hdev->set_diag = btintel_set_diag_combined; |
| hdev->set_bdaddr = btintel_set_bdaddr; |
| |
| + strncpy(coredump_info.driver_name, driver_name, DRIVER_NAME_LEN - 1); |
| + |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(btintel_configure_setup); |
| diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h |
| index 50ad1aeb36e5e3b2a2d4332237e09d352ec66c19..20c91f25d3c85d4b89782af93eb1ce270a6d5aad 100644 |
| --- a/drivers/bluetooth/btintel.h |
| +++ b/drivers/bluetooth/btintel.h |
| @@ -150,6 +150,12 @@ struct btintel_loc_aware_reg { |
| __le32 delta; |
| } __packed; |
| |
| +#define INTEL_TLV_TYPE_ID 0x1 |
| + |
| +#define INTEL_TLV_SYSTEM_EXCEPTION 0x0 |
| +#define INTEL_TLV_FATAL_EXCEPTION 0x1 |
| +#define INTEL_TLV_DEBUG_EXCEPTION 0x2 |
| + |
| #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) |
| @@ -219,7 +225,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); |
| @@ -302,7 +308,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; |
| } |
| diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c |
| index 84a6cf6b79aff644a4c4499139cd461cce376461..a10e0df0df20ab1f61d00849a5188e0724a78751 100644 |
| --- a/drivers/bluetooth/btusb.c |
| +++ b/drivers/bluetooth/btusb.c |
| @@ -2392,16 +2392,44 @@ 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: |
| + /* Generate devcoredump from exception */ |
| + if (!hci_devcoredump_init(hdev, skb->len)) { |
| + hci_devcoredump_append(hdev, skb); |
| + hci_devcoredump_complete(hdev); |
| + } else { |
| + bt_dev_err(hdev, "Failed to generate devcoredump"); |
| + kfree_skb(skb); |
| + } |
| + return 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 |
| @@ -2420,6 +2448,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); |
| @@ -4031,7 +4068,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; |
| |
| -- |
| 2.34.1 |
| |