wifi: mt76: mt7996: Use DECLARE_FLEX_ARRAY() and fix -Warray-bounds warnings
[linux-2.6-block.git] / drivers / net / wireless / ath / ath11k / fw.c
1 // SPDX-License-Identifier: BSD-3-Clause-Clear
2 /*
3  * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
4  */
5
6 #include "core.h"
7
8 #include "debug.h"
9
10 static int ath11k_fw_request_firmware_api_n(struct ath11k_base *ab,
11                                             const char *name)
12 {
13         size_t magic_len, len, ie_len;
14         int ie_id, i, index, bit, ret;
15         struct ath11k_fw_ie *hdr;
16         const u8 *data;
17         __le32 *timestamp;
18
19         ab->fw.fw = ath11k_core_firmware_request(ab, name);
20         if (IS_ERR(ab->fw.fw)) {
21                 ret = PTR_ERR(ab->fw.fw);
22                 ath11k_dbg(ab, ATH11K_DBG_BOOT, "failed to load %s: %d\n", name, ret);
23                 ab->fw.fw = NULL;
24                 return ret;
25         }
26
27         data = ab->fw.fw->data;
28         len = ab->fw.fw->size;
29
30         /* magic also includes the null byte, check that as well */
31         magic_len = strlen(ATH11K_FIRMWARE_MAGIC) + 1;
32
33         if (len < magic_len) {
34                 ath11k_err(ab, "firmware image too small to contain magic: %zu\n",
35                            len);
36                 ret = -EINVAL;
37                 goto err;
38         }
39
40         if (memcmp(data, ATH11K_FIRMWARE_MAGIC, magic_len) != 0) {
41                 ath11k_err(ab, "Invalid firmware magic\n");
42                 ret = -EINVAL;
43                 goto err;
44         }
45
46         /* jump over the padding */
47         magic_len = ALIGN(magic_len, 4);
48
49         /* make sure there's space for padding */
50         if (magic_len > len) {
51                 ath11k_err(ab, "No space for padding after magic\n");
52                 ret = -EINVAL;
53                 goto err;
54         }
55
56         len -= magic_len;
57         data += magic_len;
58
59         /* loop elements */
60         while (len > sizeof(struct ath11k_fw_ie)) {
61                 hdr = (struct ath11k_fw_ie *)data;
62
63                 ie_id = le32_to_cpu(hdr->id);
64                 ie_len = le32_to_cpu(hdr->len);
65
66                 len -= sizeof(*hdr);
67                 data += sizeof(*hdr);
68
69                 if (len < ie_len) {
70                         ath11k_err(ab, "Invalid length for FW IE %d (%zu < %zu)\n",
71                                    ie_id, len, ie_len);
72                         ret = -EINVAL;
73                         goto err;
74                 }
75
76                 switch (ie_id) {
77                 case ATH11K_FW_IE_TIMESTAMP:
78                         if (ie_len != sizeof(u32))
79                                 break;
80
81                         timestamp = (__le32 *)data;
82
83                         ath11k_dbg(ab, ATH11K_DBG_BOOT, "found fw timestamp %d\n",
84                                    le32_to_cpup(timestamp));
85                         break;
86                 case ATH11K_FW_IE_FEATURES:
87                         ath11k_dbg(ab, ATH11K_DBG_BOOT,
88                                    "found firmware features ie (%zd B)\n",
89                                    ie_len);
90
91                         for (i = 0; i < ATH11K_FW_FEATURE_COUNT; i++) {
92                                 index = i / 8;
93                                 bit = i % 8;
94
95                                 if (index == ie_len)
96                                         break;
97
98                                 if (data[index] & (1 << bit))
99                                         __set_bit(i, ab->fw.fw_features);
100                         }
101
102                         ath11k_dbg_dump(ab, ATH11K_DBG_BOOT, "features", "",
103                                         ab->fw.fw_features,
104                                         sizeof(ab->fw.fw_features));
105                         break;
106                 case ATH11K_FW_IE_AMSS_IMAGE:
107                         ath11k_dbg(ab, ATH11K_DBG_BOOT,
108                                    "found fw image ie (%zd B)\n",
109                                    ie_len);
110
111                         ab->fw.amss_data = data;
112                         ab->fw.amss_len = ie_len;
113                         break;
114                 case ATH11K_FW_IE_M3_IMAGE:
115                         ath11k_dbg(ab, ATH11K_DBG_BOOT,
116                                    "found m3 image ie (%zd B)\n",
117                                    ie_len);
118
119                         ab->fw.m3_data = data;
120                         ab->fw.m3_len = ie_len;
121                         break;
122                 default:
123                         ath11k_warn(ab, "Unknown FW IE: %u\n", ie_id);
124                         break;
125                 }
126
127                 /* jump over the padding */
128                 ie_len = ALIGN(ie_len, 4);
129
130                 /* make sure there's space for padding */
131                 if (ie_len > len)
132                         break;
133
134                 len -= ie_len;
135                 data += ie_len;
136         };
137
138         return 0;
139
140 err:
141         release_firmware(ab->fw.fw);
142         ab->fw.fw = NULL;
143         return ret;
144 }
145
146 int ath11k_fw_pre_init(struct ath11k_base *ab)
147 {
148         int ret;
149
150         ret = ath11k_fw_request_firmware_api_n(ab, ATH11K_FW_API2_FILE);
151         if (ret == 0) {
152                 ab->fw.api_version = 2;
153                 goto out;
154         }
155
156         ab->fw.api_version = 1;
157
158 out:
159         ath11k_dbg(ab, ATH11K_DBG_BOOT, "using fw api %d\n",
160                    ab->fw.api_version);
161
162         return 0;
163 }
164
165 void ath11k_fw_destroy(struct ath11k_base *ab)
166 {
167         release_firmware(ab->fw.fw);
168 }