[PATCH v1 01/11] Bluetooth: btnxpuart: Add firmware metadata parsing for secure interface

Neeraj Sanjay Kale posted 11 patches 1 week, 6 days ago
There is a newer version of this series
[PATCH v1 01/11] Bluetooth: btnxpuart: Add firmware metadata parsing for secure interface
Posted by Neeraj Sanjay Kale 1 week, 6 days ago
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
Re: [PATCH v1 01/11] Bluetooth: btnxpuart: Add firmware metadata parsing for secure interface
Posted by kernel test robot 1 week, 4 days ago
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