This adds support for parsing firmware metadata TLVs to extract FW UUID and
ECDSA Public Key from FW metadata for secure interface authentication.
Signed-off-by: Neeraj Sanjay Kale <neeraj.sanjaykale@nxp.com>
---
drivers/bluetooth/btnxpuart.c | 133 ++++++++++++++++++++++++++++++++--
1 file changed, 125 insertions(+), 8 deletions(-)
diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c
index 3b1e9224e965..3f94ca18a225 100644
--- a/drivers/bluetooth/btnxpuart.c
+++ b/drivers/bluetooth/btnxpuart.c
@@ -15,6 +15,7 @@
#include <linux/string.h>
#include <linux/crc8.h>
#include <linux/crc32.h>
+#include <linux/math.h>
#include <linux/string_helpers.h>
#include <linux/gpio/consumer.h>
#include <linux/of_irq.h>
@@ -134,6 +135,14 @@
#define BT_CTRL_WAKEUP_METHOD_EXT_BREAK 0x04
#define BT_CTRL_WAKEUP_METHOD_RTS 0x05
+/* FW Metadata */
+#define FW_METADATA_TLV_UUID 0x40
+#define FW_METADATA_TLV_ECDSA_KEY 0x50
+#define FW_METADATA_FLAG_BT 0x02
+
+#define NXP_FW_UUID_SIZE 16
+#define NXP_FW_ECDSA_PUBKEY_SIZE 65
+
struct ps_data {
u8 target_ps_mode; /* ps mode to be set */
u8 cur_psmode; /* current ps_mode */
@@ -180,6 +189,11 @@ enum bootloader_param_change {
changed
};
+struct btnxpuart_crypto {
+ u8 ecdsa_public[NXP_FW_ECDSA_PUBKEY_SIZE]; /* ECDSA public key, Authentication*/
+ u8 fw_uuid[NXP_FW_UUID_SIZE];
+};
+
struct btnxpuart_dev {
struct hci_dev *hdev;
struct serdev_device *serdev;
@@ -213,6 +227,7 @@ struct btnxpuart_dev {
struct btnxpuart_data *nxp_data;
struct reset_control *pdn;
struct hci_uart hu;
+ struct btnxpuart_crypto crypto;
};
#define NXP_V1_FW_REQ_PKT 0xa5
@@ -362,6 +377,26 @@ union nxp_set_bd_addr_payload {
u8 buf[8];
};
+/* FW Meta Data */
+struct fw_metadata_hdr {
+ __le32 cmd;
+ __le32 addr;
+ __le32 len;
+ __le32 crc;
+};
+
+struct fw_metadata_tail {
+ __le32 len;
+ u8 magic[8];
+ __le32 crc;
+};
+
+struct fw_metadata_tlv {
+ __le16 id;
+ __le16 flag;
+ __le32 len;
+};
+
static u8 crc8_table[CRC8_TABLE_SIZE];
/* Default configurations */
@@ -1190,6 +1225,85 @@ static void nxp_handle_fw_download_error(struct hci_dev *hdev, struct v3_data_re
}
}
+static u32 nxp_process_fw_metadata_tlv(struct hci_dev *hdev, char **payload)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ struct fw_metadata_tlv *tlv = (struct fw_metadata_tlv *)(*payload);
+ u32 ret = sizeof(*tlv) + le32_to_cpu(tlv->len);
+
+ /* Process only BT specific metadata TLVs */
+ if (!(le16_to_cpu(tlv->flag) & FW_METADATA_FLAG_BT))
+ goto align_and_return;
+
+ switch (le16_to_cpu(tlv->id)) {
+ case FW_METADATA_TLV_UUID:
+ if (le32_to_cpu(tlv->len) == NXP_FW_UUID_SIZE)
+ memcpy(nxpdev->crypto.fw_uuid,
+ *payload + sizeof(*tlv), NXP_FW_UUID_SIZE);
+ break;
+ case FW_METADATA_TLV_ECDSA_KEY:
+ if (le32_to_cpu(tlv->len) == NXP_FW_ECDSA_PUBKEY_SIZE)
+ memcpy(nxpdev->crypto.ecdsa_public,
+ *payload + sizeof(*tlv), NXP_FW_ECDSA_PUBKEY_SIZE);
+ break;
+ default:
+ bt_dev_err(hdev, "Unknown metadata TLV ID: 0x%x", le16_to_cpu(tlv->id));
+ break;
+ }
+
+align_and_return:
+ /* Align the pointer to 4 byte structure alignment */
+ ret = round_up(ret, 4);
+ *payload += ret;
+
+ return ret;
+}
+
+static void nxp_process_fw_meta_data(struct hci_dev *hdev, const struct firmware *fw)
+{
+ const char *metamagc = "metamagc";
+ struct fw_metadata_hdr *hdr = NULL;
+ struct fw_metadata_tail *tail;
+ u32 hdr_crc = 0;
+ u32 payload_crc = 0;
+ char *payload;
+ u32 payload_len = 0;
+
+ /* FW metadata should contain at least header and tail */
+ if (fw->size < (sizeof(*hdr) + sizeof(*tail)))
+ return;
+
+ tail = (struct fw_metadata_tail *)&fw->data[fw->size - sizeof(*tail)];
+
+ /* If tail doesn't contain the string "metamagc", this is invalid FW metadata */
+ if (memcmp(metamagc, tail->magic, strlen(metamagc)))
+ return;
+
+ hdr = (struct fw_metadata_hdr *)&fw->data[fw->size -
+ sizeof(*tail) -
+ tail->len];
+
+ /* If metadata header isn't cmd24, this is invalid FW metadata */
+ if (le32_to_cpu(hdr->cmd) != 24)
+ return;
+
+ /* If header CRC doesn't match, this is invalid FW metadata */
+ hdr_crc = crc32_be(0, (u8 *)hdr, offsetof(struct fw_metadata_hdr, crc));
+ if (hdr_crc != hdr->crc)
+ return;
+
+ /* If payload CRC doesn't match, this is invalid FW metadata */
+ payload = (u8 *)hdr + sizeof(*hdr);
+ payload_crc = crc32_be(0, payload, hdr->len - 4);
+ if (payload_crc != tail->crc)
+ return;
+
+ payload_len = hdr->len - sizeof(*tail);
+
+ while (payload_len > sizeof(struct fw_metadata_tlv))
+ payload_len -= nxp_process_fw_metadata_tlv(hdev, &payload);
+}
+
static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
@@ -1248,14 +1362,6 @@ static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb)
goto free_skb;
}
- if (req->len == 0) {
- bt_dev_info(hdev, "FW Download Complete: %zu bytes",
- nxpdev->fw->size);
- clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
- wake_up_interruptible(&nxpdev->fw_dnld_done_wait_q);
- goto free_skb;
- }
-
offset = __le32_to_cpu(req->offset);
if (offset < nxpdev->fw_v3_offset_correction) {
/* This scenario should ideally never occur. But if it ever does,
@@ -1267,6 +1373,17 @@ static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb)
}
nxpdev->fw_dnld_v3_offset = offset - nxpdev->fw_v3_offset_correction;
+
+ if (req->len == 0) {
+ if (nxpdev->fw_dnld_v3_offset < nxpdev->fw->size)
+ nxp_process_fw_meta_data(hdev, nxpdev->fw);
+ bt_dev_info(hdev, "FW Download Complete: %u bytes.",
+ req->offset - nxpdev->fw_v3_offset_correction);
+ clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
+ wake_up_interruptible(&nxpdev->fw_dnld_done_wait_q);
+ goto free_skb;
+ }
+
serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data +
nxpdev->fw_dnld_v3_offset, len);
--
2.43.0
Hi Neeraj,
kernel test robot noticed the following build warnings:
[auto build test WARNING on bluetooth/master]
[also build test WARNING on bluetooth-next/master linus/master v6.18-rc6]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Neeraj-Sanjay-Kale/Bluetooth-btnxpuart-Add-firmware-metadata-parsing-for-secure-interface/20251118-223605
base: https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git master
patch link: https://lore.kernel.org/r/20251118142025.1982263-2-neeraj.sanjaykale%40nxp.com
patch subject: [PATCH v1 01/11] Bluetooth: btnxpuart: Add firmware metadata parsing for secure interface
config: i386-randconfig-061-20251120 (https://download.01.org/0day-ci/archive/20251120/202511201312.njyFmd0I-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251120/202511201312.njyFmd0I-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202511201312.njyFmd0I-lkp@intel.com/
sparse warnings: (new ones prefixed by >>)
>> drivers/bluetooth/btnxpuart.c:1284:55: sparse: sparse: restricted __le32 degrades to integer
drivers/bluetooth/btnxpuart.c:1292:27: sparse: sparse: restricted __le32 degrades to integer
drivers/bluetooth/btnxpuart.c:1297:47: sparse: sparse: restricted __le32 degrades to integer
drivers/bluetooth/btnxpuart.c:1298:32: sparse: sparse: restricted __le32 degrades to integer
drivers/bluetooth/btnxpuart.c:1301:26: sparse: sparse: restricted __le32 degrades to integer
drivers/bluetooth/btnxpuart.c:1380:17: sparse: sparse: restricted __le32 degrades to integer
vim +1284 drivers/bluetooth/btnxpuart.c
1261
1262 static void nxp_process_fw_meta_data(struct hci_dev *hdev, const struct firmware *fw)
1263 {
1264 const char *metamagc = "metamagc";
1265 struct fw_metadata_hdr *hdr = NULL;
1266 struct fw_metadata_tail *tail;
1267 u32 hdr_crc = 0;
1268 u32 payload_crc = 0;
1269 char *payload;
1270 u32 payload_len = 0;
1271
1272 /* FW metadata should contain at least header and tail */
1273 if (fw->size < (sizeof(*hdr) + sizeof(*tail)))
1274 return;
1275
1276 tail = (struct fw_metadata_tail *)&fw->data[fw->size - sizeof(*tail)];
1277
1278 /* If tail doesn't contain the string "metamagc", this is invalid FW metadata */
1279 if (memcmp(metamagc, tail->magic, strlen(metamagc)))
1280 return;
1281
1282 hdr = (struct fw_metadata_hdr *)&fw->data[fw->size -
1283 sizeof(*tail) -
> 1284 tail->len];
1285
1286 /* If metadata header isn't cmd24, this is invalid FW metadata */
1287 if (le32_to_cpu(hdr->cmd) != 24)
1288 return;
1289
1290 /* If header CRC doesn't match, this is invalid FW metadata */
1291 hdr_crc = crc32_be(0, (u8 *)hdr, offsetof(struct fw_metadata_hdr, crc));
1292 if (hdr_crc != hdr->crc)
1293 return;
1294
1295 /* If payload CRC doesn't match, this is invalid FW metadata */
1296 payload = (u8 *)hdr + sizeof(*hdr);
1297 payload_crc = crc32_be(0, payload, hdr->len - 4);
1298 if (payload_crc != tail->crc)
1299 return;
1300
1301 payload_len = hdr->len - sizeof(*tail);
1302
1303 while (payload_len > sizeof(struct fw_metadata_tlv))
1304 payload_len -= nxp_process_fw_metadata_tlv(hdev, &payload);
1305 }
1306
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
© 2016 - 2025 Red Hat, Inc.