| From dcb9dc6a61f649e6d69068404a497234327c2dd0 Mon Sep 17 00:00:00 2001 |
| From: Abhishek Pandit-Subedi <abhishekpandit@chromium.org> |
| Date: Thu, 30 Mar 2023 09:58:26 -0700 |
| Subject: [PATCH] BACKPORT: FROMGIT: 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> |
| Reviewed-by: Chethan Tumkur Narayan <chethan.tumkur.narayan@intel.com> |
| Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> |
| |
| (cherry picked from commit 0b93eeba44548b15b44f63e5ea820fd5dab0a835 |
| git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git |
| master) |
| |
| BACKPORT Notes: btintel.h: struct btintel_loc_aware_reg not present in |
| the chromium tree. btintel.c: btintel_pull_quality_report_data() not |
| present in upstream. |
| |
| 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: I134b310d0a5c5a578b603f844ddd56e25c21e1b1 |
| Signed-off-by: Manish Mandlik <mmandlik@google.com> |
| Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/4409622 |
| Tested-by: Manish Mandlik <mmandlik@chromium.org> |
| Commit-Queue: Manish Mandlik <mmandlik@chromium.org> |
| Reviewed-by: Abhishek Pandit-Subedi <abhishekpandit@google.com> |
| --- |
| drivers/bluetooth/btintel.c | 72 ++++++++++++++++++++++++++++++++++++- |
| drivers/bluetooth/btintel.h | 12 +++++-- |
| drivers/bluetooth/btusb.c | 54 ++++++++++++++++++++++++---- |
| 3 files changed, 128 insertions(+), 10 deletions(-) |
| |
| diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c |
| index d51c1ff07c789b20175d8dc294d6f838a386a348..a774c5b632d190be096da6bfb3ab987c383bd372 100644 |
| --- a/drivers/bluetooth/btintel.c |
| +++ b/drivers/bluetooth/btintel.c |
| @@ -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; |
| @@ -316,6 +322,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, |
| @@ -511,6 +520,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); |
| @@ -1503,6 +1515,59 @@ 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) |
| +{ |
| + 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) |
| { |
| @@ -2646,6 +2711,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)", |
| @@ -2719,6 +2785,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: |
| @@ -2742,6 +2809,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)", |
| @@ -2791,7 +2859,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; |
| @@ -2800,6 +2868,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); |
| diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h |
| index 94587cfa340ef82bdd712d8015e03d2e2d66a3fb..ae79f88dd05fea1cf46de0a1f5db8cc1f923c574 100644 |
| --- a/drivers/bluetooth/btintel.h |
| +++ b/drivers/bluetooth/btintel.h |
| @@ -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) |
| @@ -213,7 +220,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); |
| @@ -296,7 +303,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 4b4126e0300e3de14f8b07ac4efc6a5aa8649c25..fec54ed0d218026e1598775a376943fb8982be2d 100644 |
| --- a/drivers/bluetooth/btusb.c |
| +++ b/drivers/bluetooth/btusb.c |
| @@ -2365,16 +2365,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 |
| @@ -2393,6 +2424,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); |
| @@ -4008,7 +4048,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 |
| |