wifi: rtw89: fw: download firmware with key data for secure boot
authorPing-Ke Shih <pkshih@realtek.com>
Sun, 4 Feb 2024 01:26:27 +0000 (09:26 +0800)
committerKalle Valo <kvalo@kernel.org>
Tue, 6 Feb 2024 18:06:14 +0000 (20:06 +0200)
Since firmware header contains multiple secure sections, we need to trim
ignored sections, and then download firmware header with single one secure
section.

For secure boot, when downloading secure section, copy security key data
from MSS poll by key_idx read from efuse. If non-secure boot, no need this
extra copy.

           +---------------------------+ -\
           |      firmware header      |  |
           |                           |  |
           | +-----------------------+ |  | only preserve single one secure
           | | section type/size * N | |  | section
           | | ...                   | |  |
           | +-----------------------+ |  |
           +---------------------------+ -/
           :                           :
           +---------------------------+ -\
           | secure section type (ID:9)|  |
           |                           |  |
      +----|-> [ security key data ]   |  |
      |    +---------------------------+ -/
      |    |MSS Pool for above section |
      |    |  [ security key data 0 ]  |
      +----|- [ security key data 1 ]  |
by key_idx |  [ security key data 2 ]  |
           |  ...                      |
           +---------------------------+

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://msgid.link/20240204012627.9647-5-pkshih@realtek.com
drivers/net/wireless/realtek/rtw89/fw.c
drivers/net/wireless/realtek/rtw89/fw.h

index 00a6cb7fcd2a77d79cb9007353661a90f3deb4a7..51072a2dcf1047a76b6b66a5bd4e3d7e5fde4b60 100644 (file)
@@ -1098,9 +1098,56 @@ static void rtw89_h2c_pkt_set_hdr_fwdl(struct rtw89_dev *rtwdev,
                                           len + H2C_HEADER_LEN));
 }
 
-static int __rtw89_fw_download_hdr(struct rtw89_dev *rtwdev, const u8 *fw, u32 len)
+static u32 __rtw89_fw_download_tweak_hdr_v0(struct rtw89_dev *rtwdev,
+                                           struct rtw89_fw_bin_info *info,
+                                           struct rtw89_fw_hdr *fw_hdr)
 {
+       le32p_replace_bits(&fw_hdr->w7, FWDL_SECTION_PER_PKT_LEN,
+                          FW_HDR_W7_PART_SIZE);
+
+       return 0;
+}
+
+static u32 __rtw89_fw_download_tweak_hdr_v1(struct rtw89_dev *rtwdev,
+                                           struct rtw89_fw_bin_info *info,
+                                           struct rtw89_fw_hdr_v1 *fw_hdr)
+{
+       struct rtw89_fw_hdr_section_info *section_info;
+       struct rtw89_fw_hdr_section_v1 *section;
+       u8 dst_sec_idx = 0;
+       u8 sec_idx;
+
+       le32p_replace_bits(&fw_hdr->w7, FWDL_SECTION_PER_PKT_LEN,
+                          FW_HDR_V1_W7_PART_SIZE);
+
+       for (sec_idx = 0; sec_idx < info->section_num; sec_idx++) {
+               section_info = &info->section_info[sec_idx];
+               section = &fw_hdr->sections[sec_idx];
+
+               if (section_info->ignore)
+                       continue;
+
+               if (dst_sec_idx != sec_idx)
+                       fw_hdr->sections[dst_sec_idx] = *section;
+
+               dst_sec_idx++;
+       }
+
+       le32p_replace_bits(&fw_hdr->w6, dst_sec_idx, FW_HDR_V1_W6_SEC_NUM);
+
+       return (info->section_num - dst_sec_idx) * sizeof(*section);
+}
+
+static int __rtw89_fw_download_hdr(struct rtw89_dev *rtwdev,
+                                  const struct rtw89_fw_suit *fw_suit,
+                                  struct rtw89_fw_bin_info *info)
+{
+       u32 len = info->hdr_len - info->dynamic_hdr_len;
+       struct rtw89_fw_hdr_v1 *fw_hdr_v1;
+       const u8 *fw = fw_suit->data;
+       struct rtw89_fw_hdr *fw_hdr;
        struct sk_buff *skb;
+       u32 truncated;
        u32 ret = 0;
 
        skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
@@ -1110,7 +1157,26 @@ static int __rtw89_fw_download_hdr(struct rtw89_dev *rtwdev, const u8 *fw, u32 l
        }
 
        skb_put_data(skb, fw, len);
-       SET_FW_HDR_PART_SIZE(skb->data, FWDL_SECTION_PER_PKT_LEN);
+
+       switch (fw_suit->hdr_ver) {
+       case 0:
+               fw_hdr = (struct rtw89_fw_hdr *)skb->data;
+               truncated = __rtw89_fw_download_tweak_hdr_v0(rtwdev, info, fw_hdr);
+               break;
+       case 1:
+               fw_hdr_v1 = (struct rtw89_fw_hdr_v1 *)skb->data;
+               truncated = __rtw89_fw_download_tweak_hdr_v1(rtwdev, info, fw_hdr_v1);
+               break;
+       default:
+               ret = -EOPNOTSUPP;
+               goto fail;
+       }
+
+       if (truncated) {
+               len -= truncated;
+               skb_trim(skb, len);
+       }
+
        rtw89_h2c_pkt_set_hdr_fwdl(rtwdev, skb, FWCMD_TYPE_H2C,
                                   H2C_CAT_MAC, H2C_CL_MAC_FWDL,
                                   H2C_FUNC_MAC_FWHDR_DL, len);
@@ -1129,12 +1195,14 @@ fail:
        return ret;
 }
 
-static int rtw89_fw_download_hdr(struct rtw89_dev *rtwdev, const u8 *fw, u32 len)
+static int rtw89_fw_download_hdr(struct rtw89_dev *rtwdev,
+                                const struct rtw89_fw_suit *fw_suit,
+                                struct rtw89_fw_bin_info *info)
 {
        const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
        int ret;
 
-       ret = __rtw89_fw_download_hdr(rtwdev, fw, len);
+       ret = __rtw89_fw_download_hdr(rtwdev, fw_suit, info);
        if (ret) {
                rtw89_err(rtwdev, "[ERR]FW header download\n");
                return ret;
@@ -1158,9 +1226,21 @@ static int __rtw89_fw_download_main(struct rtw89_dev *rtwdev,
        struct sk_buff *skb;
        const u8 *section = info->addr;
        u32 residue_len = info->len;
+       bool copy_key = false;
        u32 pkt_len;
        int ret;
 
+       if (info->ignore)
+               return 0;
+
+       if (info->key_addr && info->key_len) {
+               if (info->len > FWDL_SECTION_PER_PKT_LEN || info->len < info->key_len)
+                       rtw89_warn(rtwdev, "ignore to copy key data because of len %d, %d, %d\n",
+                                  info->len, FWDL_SECTION_PER_PKT_LEN, info->key_len);
+               else
+                       copy_key = true;
+       }
+
        while (residue_len) {
                if (residue_len >= FWDL_SECTION_PER_PKT_LEN)
                        pkt_len = FWDL_SECTION_PER_PKT_LEN;
@@ -1174,6 +1254,10 @@ static int __rtw89_fw_download_main(struct rtw89_dev *rtwdev,
                }
                skb_put_data(skb, section, pkt_len);
 
+               if (copy_key)
+                       memcpy(skb->data + pkt_len - info->key_len,
+                              info->key_addr, info->key_len);
+
                ret = rtw89_h2c_tx(rtwdev, skb, true);
                if (ret) {
                        rtw89_err(rtwdev, "failed to send h2c\n");
@@ -1299,8 +1383,7 @@ static int rtw89_fw_download_suit(struct rtw89_dev *rtwdev,
                return ret;
        }
 
-       ret = rtw89_fw_download_hdr(rtwdev, fw_suit->data, info.hdr_len -
-                                                          info.dynamic_hdr_len);
+       ret = rtw89_fw_download_hdr(rtwdev, fw_suit, &info);
        if (ret)
                return ret;
 
index 58dbaf7a11e7ca5fe8efdf5e9cfe848ed479d047..5609e5f7d7eb6d9a44662a72a2879d65663ea0c5 100644 (file)
@@ -526,6 +526,7 @@ struct rtw89_fw_hdr {
 #define FW_HDR_W4_MIN GENMASK(31, 24)
 #define FW_HDR_W5_YEAR GENMASK(31, 0)
 #define FW_HDR_W6_SEC_NUM GENMASK(15, 8)
+#define FW_HDR_W7_PART_SIZE GENMASK(15, 0)
 #define FW_HDR_W7_DYN_HDR BIT(16)
 #define FW_HDR_W7_CMD_VERSERION GENMASK(31, 24)
 
@@ -577,13 +578,9 @@ struct rtw89_fw_hdr_v1 {
 #define FW_HDR_V1_W5_HDR_SIZE GENMASK(31, 16)
 #define FW_HDR_V1_W6_SEC_NUM GENMASK(15, 8)
 #define FW_HDR_V1_W6_DSP_CHKSUM BIT(24)
+#define FW_HDR_V1_W7_PART_SIZE GENMASK(15, 0)
 #define FW_HDR_V1_W7_DYN_HDR BIT(16)
 
-static inline void SET_FW_HDR_PART_SIZE(void *fwhdr, u32 val)
-{
-       le32p_replace_bits((__le32 *)fwhdr + 7, val, GENMASK(15, 0));
-}
-
 enum rtw89_fw_mss_pool_rmp_tbl_type {
        MSS_POOL_RMP_TBL_BITMASK = 0x0,
        MSS_POOL_RMP_TBL_RECORD = 0x1,