1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3  * Copyright(c) 2020-2021 Intel Corporation
4  */
5 
6 #include "iwl-drv.h"
7 #include "pnvm.h"
8 #include "iwl-prph.h"
9 #include "iwl-io.h"
10 #include "fw/api/commands.h"
11 #include "fw/api/nvm-reg.h"
12 #include "fw/api/alive.h"
13 #include "fw/uefi.h"
14 
15 struct iwl_pnvm_section {
16 	__le32 offset;
17 	const u8 data[];
18 } __packed;
19 
20 struct pnvm_sku_package {
21 	u8 rev;
22 	u8 reserved1[3];
23 	u32 total_size;
24 	u8 n_skus;
25 	u8 reserved2[11];
26 	u8 data[];
27 };
28 
29 static bool iwl_pnvm_complete_fn(struct iwl_notif_wait_data *notif_wait,
30 				 struct iwl_rx_packet *pkt, void *data)
31 {
32 	struct iwl_trans *trans = (struct iwl_trans *)data;
33 	struct iwl_pnvm_init_complete_ntfy *pnvm_ntf = (void *)pkt->data;
34 
35 	IWL_DEBUG_FW(trans,
36 		     "PNVM complete notification received with status %d\n",
37 		     le32_to_cpu(pnvm_ntf->status));
38 
39 	return true;
40 }
41 
42 static int iwl_pnvm_handle_section(struct iwl_trans *trans, const u8 *data,
43 				   size_t len)
44 {
45 	struct iwl_ucode_tlv *tlv;
46 	u32 sha1 = 0;
47 	u16 mac_type = 0, rf_id = 0;
48 	u8 *pnvm_data = NULL, *tmp;
49 	u32 size = 0;
50 	int ret;
51 
52 	IWL_DEBUG_FW(trans, "Handling PNVM section\n");
53 
54 	while (len >= sizeof(*tlv)) {
55 		u32 tlv_len, tlv_type;
56 
57 		len -= sizeof(*tlv);
58 		tlv = (void *)data;
59 
60 		tlv_len = le32_to_cpu(tlv->length);
61 		tlv_type = le32_to_cpu(tlv->type);
62 
63 		if (len < tlv_len) {
64 			IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
65 				len, tlv_len);
66 			ret = -EINVAL;
67 			goto out;
68 		}
69 
70 		data += sizeof(*tlv);
71 
72 		switch (tlv_type) {
73 		case IWL_UCODE_TLV_PNVM_VERSION:
74 			if (tlv_len < sizeof(__le32)) {
75 				IWL_DEBUG_FW(trans,
76 					     "Invalid size for IWL_UCODE_TLV_PNVM_VERSION (expected %zd, got %d)\n",
77 					     sizeof(__le32), tlv_len);
78 				break;
79 			}
80 
81 			sha1 = le32_to_cpup((__le32 *)data);
82 
83 			IWL_DEBUG_FW(trans,
84 				     "Got IWL_UCODE_TLV_PNVM_VERSION %0x\n",
85 				     sha1);
86 			break;
87 		case IWL_UCODE_TLV_HW_TYPE:
88 			if (tlv_len < 2 * sizeof(__le16)) {
89 				IWL_DEBUG_FW(trans,
90 					     "Invalid size for IWL_UCODE_TLV_HW_TYPE (expected %zd, got %d)\n",
91 					     2 * sizeof(__le16), tlv_len);
92 				break;
93 			}
94 
95 			mac_type = le16_to_cpup((__le16 *)data);
96 			rf_id = le16_to_cpup((__le16 *)(data + sizeof(__le16)));
97 
98 			IWL_DEBUG_FW(trans,
99 				     "Got IWL_UCODE_TLV_HW_TYPE mac_type 0x%0x rf_id 0x%0x\n",
100 				     mac_type, rf_id);
101 
102 			if (mac_type != CSR_HW_REV_TYPE(trans->hw_rev) ||
103 			    rf_id != CSR_HW_RFID_TYPE(trans->hw_rf_id)) {
104 				IWL_DEBUG_FW(trans,
105 					     "HW mismatch, skipping PNVM section, mac_type 0x%0x, rf_id 0x%0x.\n",
106 					     CSR_HW_REV_TYPE(trans->hw_rev), trans->hw_rf_id);
107 				ret = -ENOENT;
108 				goto out;
109 			}
110 
111 			break;
112 		case IWL_UCODE_TLV_SEC_RT: {
113 			struct iwl_pnvm_section *section = (void *)data;
114 			u32 data_len = tlv_len - sizeof(*section);
115 
116 			IWL_DEBUG_FW(trans,
117 				     "Got IWL_UCODE_TLV_SEC_RT len %d\n",
118 				     tlv_len);
119 
120 			/* TODO: remove, this is a deprecated separator */
121 			if (le32_to_cpup((__le32 *)data) == 0xddddeeee) {
122 				IWL_DEBUG_FW(trans, "Ignoring separator.\n");
123 				break;
124 			}
125 
126 			IWL_DEBUG_FW(trans, "Adding data (size %d)\n",
127 				     data_len);
128 
129 			tmp = krealloc(pnvm_data, size + data_len, GFP_KERNEL);
130 			if (!tmp) {
131 				IWL_DEBUG_FW(trans,
132 					     "Couldn't allocate (more) pnvm_data\n");
133 
134 				ret = -ENOMEM;
135 				goto out;
136 			}
137 
138 			pnvm_data = tmp;
139 
140 			memcpy(pnvm_data + size, section->data, data_len);
141 
142 			size += data_len;
143 
144 			break;
145 		}
146 		case IWL_UCODE_TLV_PNVM_SKU:
147 			IWL_DEBUG_FW(trans,
148 				     "New PNVM section started, stop parsing.\n");
149 			goto done;
150 		default:
151 			IWL_DEBUG_FW(trans, "Found TLV 0x%0x, len %d\n",
152 				     tlv_type, tlv_len);
153 			break;
154 		}
155 
156 		len -= ALIGN(tlv_len, 4);
157 		data += ALIGN(tlv_len, 4);
158 	}
159 
160 done:
161 	if (!size) {
162 		IWL_DEBUG_FW(trans, "Empty PNVM, skipping.\n");
163 		ret = -ENOENT;
164 		goto out;
165 	}
166 
167 	IWL_INFO(trans, "loaded PNVM version 0x%0x\n", sha1);
168 
169 	ret = iwl_trans_set_pnvm(trans, pnvm_data, size);
170 out:
171 	kfree(pnvm_data);
172 	return ret;
173 }
174 
175 static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data,
176 			  size_t len)
177 {
178 	struct iwl_ucode_tlv *tlv;
179 
180 	IWL_DEBUG_FW(trans, "Parsing PNVM file\n");
181 
182 	while (len >= sizeof(*tlv)) {
183 		u32 tlv_len, tlv_type;
184 
185 		len -= sizeof(*tlv);
186 		tlv = (void *)data;
187 
188 		tlv_len = le32_to_cpu(tlv->length);
189 		tlv_type = le32_to_cpu(tlv->type);
190 
191 		if (len < tlv_len) {
192 			IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
193 				len, tlv_len);
194 			return -EINVAL;
195 		}
196 
197 		if (tlv_type == IWL_UCODE_TLV_PNVM_SKU) {
198 			struct iwl_sku_id *sku_id =
199 				(void *)(data + sizeof(*tlv));
200 
201 			IWL_DEBUG_FW(trans,
202 				     "Got IWL_UCODE_TLV_PNVM_SKU len %d\n",
203 				     tlv_len);
204 			IWL_DEBUG_FW(trans, "sku_id 0x%0x 0x%0x 0x%0x\n",
205 				     le32_to_cpu(sku_id->data[0]),
206 				     le32_to_cpu(sku_id->data[1]),
207 				     le32_to_cpu(sku_id->data[2]));
208 
209 			data += sizeof(*tlv) + ALIGN(tlv_len, 4);
210 			len -= ALIGN(tlv_len, 4);
211 
212 			if (trans->sku_id[0] == le32_to_cpu(sku_id->data[0]) &&
213 			    trans->sku_id[1] == le32_to_cpu(sku_id->data[1]) &&
214 			    trans->sku_id[2] == le32_to_cpu(sku_id->data[2])) {
215 				int ret;
216 
217 				ret = iwl_pnvm_handle_section(trans, data, len);
218 				if (!ret)
219 					return 0;
220 			} else {
221 				IWL_DEBUG_FW(trans, "SKU ID didn't match!\n");
222 			}
223 		} else {
224 			data += sizeof(*tlv) + ALIGN(tlv_len, 4);
225 			len -= ALIGN(tlv_len, 4);
226 		}
227 	}
228 
229 	return -ENOENT;
230 }
231 
232 static int iwl_pnvm_get_from_fs(struct iwl_trans *trans, u8 **data, size_t *len)
233 {
234 	const struct firmware *pnvm;
235 	char pnvm_name[64];
236 	int ret;
237 
238 	/*
239 	 * The prefix unfortunately includes a hyphen at the end, so
240 	 * don't add the dot here...
241 	 */
242 	snprintf(pnvm_name, sizeof(pnvm_name), "%spnvm",
243 		 trans->cfg->fw_name_pre);
244 
245 	/* ...but replace the hyphen with the dot here. */
246 	if (strlen(trans->cfg->fw_name_pre) < sizeof(pnvm_name))
247 		pnvm_name[strlen(trans->cfg->fw_name_pre) - 1] = '.';
248 
249 	ret = firmware_request_nowarn(&pnvm, pnvm_name, trans->dev);
250 	if (ret) {
251 		IWL_DEBUG_FW(trans, "PNVM file %s not found %d\n",
252 			     pnvm_name, ret);
253 		return ret;
254 	}
255 
256 	*data = kmemdup(pnvm->data, pnvm->size, GFP_KERNEL);
257 	if (!*data)
258 		return -ENOMEM;
259 
260 	*len = pnvm->size;
261 
262 	return 0;
263 }
264 
265 int iwl_pnvm_load(struct iwl_trans *trans,
266 		  struct iwl_notif_wait_data *notif_wait)
267 {
268 	u8 *data;
269 	size_t len;
270 	struct pnvm_sku_package *package;
271 	struct iwl_notification_wait pnvm_wait;
272 	static const u16 ntf_cmds[] = { WIDE_ID(REGULATORY_AND_NVM_GROUP,
273 						PNVM_INIT_COMPLETE_NTFY) };
274 	int ret;
275 
276 	/* if the SKU_ID is empty, there's nothing to do */
277 	if (!trans->sku_id[0] && !trans->sku_id[1] && !trans->sku_id[2])
278 		return 0;
279 
280 	/*
281 	 * If we already loaded (or tried to load) it before, we just
282 	 * need to set it again.
283 	 */
284 	if (trans->pnvm_loaded) {
285 		ret = iwl_trans_set_pnvm(trans, NULL, 0);
286 		if (ret)
287 			return ret;
288 		goto skip_parse;
289 	}
290 
291 	/* First attempt to get the PNVM from BIOS */
292 	package = iwl_uefi_get_pnvm(trans, &len);
293 	if (!IS_ERR_OR_NULL(package)) {
294 		data = kmemdup(package->data, len, GFP_KERNEL);
295 
296 		/* free package regardless of whether kmemdup succeeded */
297 		kfree(package);
298 
299 		if (data) {
300 			/* we need only the data size */
301 			len -= sizeof(*package);
302 			goto parse;
303 		}
304 	}
305 
306 	/* If it's not available, try from the filesystem */
307 	ret = iwl_pnvm_get_from_fs(trans, &data, &len);
308 	if (ret) {
309 		/*
310 		 * Pretend we've loaded it - at least we've tried and
311 		 * couldn't load it at all, so there's no point in
312 		 * trying again over and over.
313 		 */
314 		trans->pnvm_loaded = true;
315 
316 		goto skip_parse;
317 	}
318 
319 parse:
320 	iwl_pnvm_parse(trans, data, len);
321 
322 	kfree(data);
323 
324 skip_parse:
325 	iwl_init_notification_wait(notif_wait, &pnvm_wait,
326 				   ntf_cmds, ARRAY_SIZE(ntf_cmds),
327 				   iwl_pnvm_complete_fn, trans);
328 
329 	/* kick the doorbell */
330 	iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6,
331 			    UREG_DOORBELL_TO_ISR6_PNVM);
332 
333 	return iwl_wait_notification(notif_wait, &pnvm_wait,
334 				     MVM_UCODE_PNVM_TIMEOUT);
335 }
336 IWL_EXPORT_SYMBOL(iwl_pnvm_load);
337