xref: /linux/net/ethtool/cmis_cdb.c (revision d53b8e36925256097a08d7cb749198d85cbf9b2b)
1 // SPDX-License-Identifier: GPL-2.0-only
2 
3 #include <linux/ethtool.h>
4 #include <linux/jiffies.h>
5 
6 #include "common.h"
7 #include "module_fw.h"
8 #include "cmis.h"
9 
10 /* For accessing the LPL field on page 9Fh, the allowable length extension is
11  * min(i, 15) byte octets where i specifies the allowable additional number of
12  * byte octets in a READ or a WRITE.
13  */
14 u32 ethtool_cmis_get_max_payload_size(u8 num_of_byte_octs)
15 {
16 	return 8 * (1 + min_t(u8, num_of_byte_octs, 15));
17 }
18 
19 void ethtool_cmis_cdb_compose_args(struct ethtool_cmis_cdb_cmd_args *args,
20 				   enum ethtool_cmis_cdb_cmd_id cmd, u8 *pl,
21 				   u8 lpl_len, u16 max_duration,
22 				   u8 read_write_len_ext, u16 msleep_pre_rpl,
23 				   u8 rpl_exp_len, u8 flags)
24 {
25 	args->req.id = cpu_to_be16(cmd);
26 	args->req.lpl_len = lpl_len;
27 	if (pl)
28 		memcpy(args->req.payload, pl, args->req.lpl_len);
29 
30 	args->max_duration = max_duration;
31 	args->read_write_len_ext =
32 		ethtool_cmis_get_max_payload_size(read_write_len_ext);
33 	args->msleep_pre_rpl = msleep_pre_rpl;
34 	args->rpl_exp_len = rpl_exp_len;
35 	args->flags = flags;
36 	args->err_msg = NULL;
37 }
38 
39 void ethtool_cmis_page_init(struct ethtool_module_eeprom *page_data,
40 			    u8 page, u32 offset, u32 length)
41 {
42 	page_data->page = page;
43 	page_data->offset = offset;
44 	page_data->length = length;
45 	page_data->i2c_address = ETHTOOL_CMIS_CDB_PAGE_I2C_ADDR;
46 }
47 
48 #define CMIS_REVISION_PAGE	0x00
49 #define CMIS_REVISION_OFFSET	0x01
50 
51 struct cmis_rev_rpl {
52 	u8 rev;
53 };
54 
55 static u8 cmis_rev_rpl_major(struct cmis_rev_rpl *rpl)
56 {
57 	return rpl->rev >> 4;
58 }
59 
60 static int cmis_rev_major_get(struct net_device *dev, u8 *rev_major)
61 {
62 	const struct ethtool_ops *ops = dev->ethtool_ops;
63 	struct ethtool_module_eeprom page_data = {0};
64 	struct netlink_ext_ack extack = {};
65 	struct cmis_rev_rpl rpl = {};
66 	int err;
67 
68 	ethtool_cmis_page_init(&page_data, CMIS_REVISION_PAGE,
69 			       CMIS_REVISION_OFFSET, sizeof(rpl));
70 	page_data.data = (u8 *)&rpl;
71 
72 	err = ops->get_module_eeprom_by_page(dev, &page_data, &extack);
73 	if (err < 0) {
74 		if (extack._msg)
75 			netdev_err(dev, "%s\n", extack._msg);
76 		return err;
77 	}
78 
79 	*rev_major = cmis_rev_rpl_major(&rpl);
80 
81 	return 0;
82 }
83 
84 #define CMIS_CDB_ADVERTISEMENT_PAGE	0x01
85 #define CMIS_CDB_ADVERTISEMENT_OFFSET	0xA3
86 
87 /* Based on section 8.4.11 "CDB Messaging Support Advertisement" in CMIS
88  * standard revision 5.2.
89  */
90 struct cmis_cdb_advert_rpl {
91 	u8	inst_supported;
92 	u8	read_write_len_ext;
93 	u8	resv1;
94 	u8	resv2;
95 };
96 
97 static u8 cmis_cdb_advert_rpl_inst_supported(struct cmis_cdb_advert_rpl *rpl)
98 {
99 	return rpl->inst_supported >> 6;
100 }
101 
102 static int cmis_cdb_advertisement_get(struct ethtool_cmis_cdb *cdb,
103 				      struct net_device *dev)
104 {
105 	const struct ethtool_ops *ops = dev->ethtool_ops;
106 	struct ethtool_module_eeprom page_data = {};
107 	struct cmis_cdb_advert_rpl rpl = {};
108 	struct netlink_ext_ack extack = {};
109 	int err;
110 
111 	ethtool_cmis_page_init(&page_data, CMIS_CDB_ADVERTISEMENT_PAGE,
112 			       CMIS_CDB_ADVERTISEMENT_OFFSET, sizeof(rpl));
113 	page_data.data = (u8 *)&rpl;
114 
115 	err = ops->get_module_eeprom_by_page(dev, &page_data, &extack);
116 	if (err < 0) {
117 		if (extack._msg)
118 			netdev_err(dev, "%s\n", extack._msg);
119 		return err;
120 	}
121 
122 	if (!cmis_cdb_advert_rpl_inst_supported(&rpl))
123 		return -EOPNOTSUPP;
124 
125 	cdb->read_write_len_ext = rpl.read_write_len_ext;
126 
127 	return 0;
128 }
129 
130 #define CMIS_PASSWORD_ENTRY_PAGE	0x00
131 #define CMIS_PASSWORD_ENTRY_OFFSET	0x7A
132 
133 struct cmis_password_entry_pl {
134 	__be32 password;
135 };
136 
137 /* See section 9.3.1 "CMD 0000h: Query Status" in CMIS standard revision 5.2.
138  * struct cmis_cdb_query_status_pl and struct cmis_cdb_query_status_rpl are
139  * structured layouts of the flat arrays,
140  * struct ethtool_cmis_cdb_request::payload and
141  * struct ethtool_cmis_cdb_rpl::payload respectively.
142  */
143 struct cmis_cdb_query_status_pl {
144 	u16 response_delay;
145 };
146 
147 struct cmis_cdb_query_status_rpl {
148 	u8 length;
149 	u8 status;
150 };
151 
152 static int
153 cmis_cdb_validate_password(struct ethtool_cmis_cdb *cdb,
154 			   struct net_device *dev,
155 			   const struct ethtool_module_fw_flash_params *params,
156 			   struct ethnl_module_fw_flash_ntf_params *ntf_params)
157 {
158 	const struct ethtool_ops *ops = dev->ethtool_ops;
159 	struct cmis_cdb_query_status_pl qs_pl = {0};
160 	struct ethtool_module_eeprom page_data = {};
161 	struct ethtool_cmis_cdb_cmd_args args = {};
162 	struct cmis_password_entry_pl pe_pl = {};
163 	struct cmis_cdb_query_status_rpl *rpl;
164 	struct netlink_ext_ack extack = {};
165 	int err;
166 
167 	ethtool_cmis_page_init(&page_data, CMIS_PASSWORD_ENTRY_PAGE,
168 			       CMIS_PASSWORD_ENTRY_OFFSET, sizeof(pe_pl));
169 	page_data.data = (u8 *)&pe_pl;
170 
171 	pe_pl = *((struct cmis_password_entry_pl *)page_data.data);
172 	pe_pl.password = params->password;
173 	err = ops->set_module_eeprom_by_page(dev, &page_data, &extack);
174 	if (err < 0) {
175 		if (extack._msg)
176 			netdev_err(dev, "%s\n", extack._msg);
177 		return err;
178 	}
179 
180 	ethtool_cmis_cdb_compose_args(&args, ETHTOOL_CMIS_CDB_CMD_QUERY_STATUS,
181 				      (u8 *)&qs_pl, sizeof(qs_pl), 0,
182 				      cdb->read_write_len_ext, 1000,
183 				      sizeof(*rpl),
184 				      CDB_F_COMPLETION_VALID | CDB_F_STATUS_VALID);
185 
186 	err = ethtool_cmis_cdb_execute_cmd(dev, &args);
187 	if (err < 0) {
188 		ethnl_module_fw_flash_ntf_err(dev, ntf_params,
189 					      "Query Status command failed",
190 					      args.err_msg);
191 		return err;
192 	}
193 
194 	rpl = (struct cmis_cdb_query_status_rpl *)args.req.payload;
195 	if (!rpl->length || !rpl->status) {
196 		ethnl_module_fw_flash_ntf_err(dev, ntf_params,
197 					      "Password was not accepted",
198 					      NULL);
199 		return -EINVAL;
200 	}
201 
202 	return 0;
203 }
204 
205 /* Some CDB commands asserts the CDB completion flag only from CMIS
206  * revision 5. Therefore, check the relevant validity flag only when
207  * the revision supports it.
208  */
209 void ethtool_cmis_cdb_check_completion_flag(u8 cmis_rev, u8 *flags)
210 {
211 	*flags |= cmis_rev >= 5 ? CDB_F_COMPLETION_VALID : 0;
212 }
213 
214 #define CMIS_CDB_MODULE_FEATURES_RESV_DATA	34
215 
216 /* See section 9.4.1 "CMD 0040h: Module Features" in CMIS standard revision 5.2.
217  * struct cmis_cdb_module_features_rpl is structured layout of the flat
218  * array, ethtool_cmis_cdb_rpl::payload.
219  */
220 struct cmis_cdb_module_features_rpl {
221 	u8	resv1[CMIS_CDB_MODULE_FEATURES_RESV_DATA];
222 	__be16	max_completion_time;
223 };
224 
225 static u16
226 cmis_cdb_module_features_completion_time(struct cmis_cdb_module_features_rpl *rpl)
227 {
228 	return be16_to_cpu(rpl->max_completion_time);
229 }
230 
231 static int cmis_cdb_module_features_get(struct ethtool_cmis_cdb *cdb,
232 					struct net_device *dev,
233 					struct ethnl_module_fw_flash_ntf_params *ntf_params)
234 {
235 	struct ethtool_cmis_cdb_cmd_args args = {};
236 	struct cmis_cdb_module_features_rpl *rpl;
237 	u8 flags = CDB_F_STATUS_VALID;
238 	int err;
239 
240 	ethtool_cmis_cdb_check_completion_flag(cdb->cmis_rev, &flags);
241 	ethtool_cmis_cdb_compose_args(&args,
242 				      ETHTOOL_CMIS_CDB_CMD_MODULE_FEATURES,
243 				      NULL, 0, 0, cdb->read_write_len_ext,
244 				      1000, sizeof(*rpl), flags);
245 
246 	err = ethtool_cmis_cdb_execute_cmd(dev, &args);
247 	if (err < 0) {
248 		ethnl_module_fw_flash_ntf_err(dev, ntf_params,
249 					      "Module Features command failed",
250 					      args.err_msg);
251 		return err;
252 	}
253 
254 	rpl = (struct cmis_cdb_module_features_rpl *)args.req.payload;
255 	cdb->max_completion_time =
256 		cmis_cdb_module_features_completion_time(rpl);
257 
258 	return 0;
259 }
260 
261 struct ethtool_cmis_cdb *
262 ethtool_cmis_cdb_init(struct net_device *dev,
263 		      const struct ethtool_module_fw_flash_params *params,
264 		      struct ethnl_module_fw_flash_ntf_params *ntf_params)
265 {
266 	struct ethtool_cmis_cdb *cdb;
267 	int err;
268 
269 	cdb = kzalloc(sizeof(*cdb), GFP_KERNEL);
270 	if (!cdb)
271 		return ERR_PTR(-ENOMEM);
272 
273 	err = cmis_rev_major_get(dev, &cdb->cmis_rev);
274 	if (err < 0)
275 		goto err;
276 
277 	if (cdb->cmis_rev < 4) {
278 		ethnl_module_fw_flash_ntf_err(dev, ntf_params,
279 					      "CMIS revision doesn't support module firmware flashing",
280 					      NULL);
281 		err = -EOPNOTSUPP;
282 		goto err;
283 	}
284 
285 	err = cmis_cdb_advertisement_get(cdb, dev);
286 	if (err < 0)
287 		goto err;
288 
289 	if (params->password_valid) {
290 		err = cmis_cdb_validate_password(cdb, dev, params, ntf_params);
291 		if (err < 0)
292 			goto err;
293 	}
294 
295 	err = cmis_cdb_module_features_get(cdb, dev, ntf_params);
296 	if (err < 0)
297 		goto err;
298 
299 	return cdb;
300 
301 err:
302 	ethtool_cmis_cdb_fini(cdb);
303 	return ERR_PTR(err);
304 }
305 
306 void ethtool_cmis_cdb_fini(struct ethtool_cmis_cdb *cdb)
307 {
308 	kfree(cdb);
309 }
310 
311 static bool is_completed(u8 data)
312 {
313 	return !!(data & 0x40);
314 }
315 
316 #define CMIS_CDB_STATUS_SUCCESS	0x01
317 
318 static bool status_success(u8 data)
319 {
320 	return data == CMIS_CDB_STATUS_SUCCESS;
321 }
322 
323 #define CMIS_CDB_STATUS_FAIL	0x40
324 
325 static bool status_fail(u8 data)
326 {
327 	return data & CMIS_CDB_STATUS_FAIL;
328 }
329 
330 struct cmis_wait_for_cond_rpl {
331 	u8 state;
332 };
333 
334 static int
335 ethtool_cmis_module_poll(struct net_device *dev,
336 			 struct cmis_wait_for_cond_rpl *rpl, u32 offset,
337 			 bool (*cond_success)(u8), bool (*cond_fail)(u8))
338 {
339 	const struct ethtool_ops *ops = dev->ethtool_ops;
340 	struct ethtool_module_eeprom page_data = {0};
341 	struct netlink_ext_ack extack = {};
342 	int err;
343 
344 	ethtool_cmis_page_init(&page_data, 0, offset, sizeof(rpl));
345 	page_data.data = (u8 *)rpl;
346 
347 	err = ops->get_module_eeprom_by_page(dev, &page_data, &extack);
348 	if (err < 0) {
349 		if (extack._msg)
350 			netdev_err_once(dev, "%s\n", extack._msg);
351 		return -EBUSY;
352 	}
353 
354 	if ((*cond_success)(rpl->state))
355 		return 0;
356 
357 	if (*cond_fail && (*cond_fail)(rpl->state))
358 		return -EIO;
359 
360 	return -EBUSY;
361 }
362 
363 int ethtool_cmis_wait_for_cond(struct net_device *dev, u8 flags, u8 flag,
364 			       u16 max_duration, u32 offset,
365 			       bool (*cond_success)(u8), bool (*cond_fail)(u8),
366 			       u8 *state)
367 {
368 	struct cmis_wait_for_cond_rpl rpl = {};
369 	unsigned long end;
370 	int err;
371 
372 	if (!(flags & flag))
373 		return 0;
374 
375 	if (max_duration == 0)
376 		max_duration = U16_MAX;
377 
378 	end = jiffies + msecs_to_jiffies(max_duration);
379 	do {
380 		err = ethtool_cmis_module_poll(dev, &rpl, offset, cond_success,
381 					       cond_fail);
382 		if (err != -EBUSY)
383 			goto out;
384 
385 		msleep(20);
386 	} while (time_before(jiffies, end));
387 
388 	err = ethtool_cmis_module_poll(dev, &rpl, offset, cond_success,
389 				       cond_fail);
390 	if (err == -EBUSY)
391 		err = -ETIMEDOUT;
392 
393 out:
394 	*state = rpl.state;
395 	return err;
396 }
397 
398 #define CMIS_CDB_COMPLETION_FLAG_OFFSET	0x08
399 
400 static int cmis_cdb_wait_for_completion(struct net_device *dev,
401 					struct ethtool_cmis_cdb_cmd_args *args)
402 {
403 	u8 flag;
404 	int err;
405 
406 	/* Some vendors demand waiting time before checking completion flag
407 	 * in some CDB commands.
408 	 */
409 	msleep(args->msleep_pre_rpl);
410 
411 	err = ethtool_cmis_wait_for_cond(dev, args->flags,
412 					 CDB_F_COMPLETION_VALID,
413 					 args->max_duration,
414 					 CMIS_CDB_COMPLETION_FLAG_OFFSET,
415 					 is_completed, NULL, &flag);
416 	if (err < 0)
417 		args->err_msg = "Completion Flag did not set on time";
418 
419 	return err;
420 }
421 
422 #define CMIS_CDB_STATUS_OFFSET	0x25
423 
424 static void cmis_cdb_status_fail_msg_get(u8 status, char **err_msg)
425 {
426 	switch (status) {
427 	case 0b10000001:
428 		*err_msg = "CDB Status is in progress: Busy capturing command";
429 		break;
430 	case 0b10000010:
431 		*err_msg =
432 			"CDB Status is in progress: Busy checking/validating command";
433 		break;
434 	case 0b10000011:
435 		*err_msg = "CDB Status is in progress: Busy executing";
436 		break;
437 	case 0b01000000:
438 		*err_msg = "CDB status failed: no specific failure";
439 		break;
440 	case 0b01000010:
441 		*err_msg =
442 			"CDB status failed: Parameter range error or parameter not supported";
443 		break;
444 	case 0b01000101:
445 		*err_msg = "CDB status failed: CdbChkCode error";
446 		break;
447 	default:
448 		*err_msg = "Unknown failure reason";
449 	}
450 };
451 
452 static int cmis_cdb_wait_for_status(struct net_device *dev,
453 				    struct ethtool_cmis_cdb_cmd_args *args)
454 {
455 	u8 status;
456 	int err;
457 
458 	/* Some vendors demand waiting time before checking status in some
459 	 * CDB commands.
460 	 */
461 	msleep(args->msleep_pre_rpl);
462 
463 	err = ethtool_cmis_wait_for_cond(dev, args->flags, CDB_F_STATUS_VALID,
464 					 args->max_duration,
465 					 CMIS_CDB_STATUS_OFFSET,
466 					 status_success, status_fail, &status);
467 	if (err < 0 && !args->err_msg)
468 		cmis_cdb_status_fail_msg_get(status, &args->err_msg);
469 
470 	return err;
471 }
472 
473 #define CMIS_CDB_REPLY_OFFSET	0x86
474 
475 static int cmis_cdb_process_reply(struct net_device *dev,
476 				  struct ethtool_module_eeprom *page_data,
477 				  struct ethtool_cmis_cdb_cmd_args *args)
478 {
479 	u8 rpl_hdr_len = sizeof(struct ethtool_cmis_cdb_rpl_hdr);
480 	u8 rpl_exp_len = args->rpl_exp_len + rpl_hdr_len;
481 	const struct ethtool_ops *ops = dev->ethtool_ops;
482 	struct netlink_ext_ack extack = {};
483 	struct ethtool_cmis_cdb_rpl *rpl;
484 	int err;
485 
486 	if (!args->rpl_exp_len)
487 		return 0;
488 
489 	ethtool_cmis_page_init(page_data, ETHTOOL_CMIS_CDB_CMD_PAGE,
490 			       CMIS_CDB_REPLY_OFFSET, rpl_exp_len);
491 	page_data->data = kmalloc(page_data->length, GFP_KERNEL);
492 	if (!page_data->data)
493 		return -ENOMEM;
494 
495 	err = ops->get_module_eeprom_by_page(dev, page_data, &extack);
496 	if (err < 0) {
497 		if (extack._msg)
498 			netdev_err(dev, "%s\n", extack._msg);
499 		goto out;
500 	}
501 
502 	rpl = (struct ethtool_cmis_cdb_rpl *)page_data->data;
503 	if ((args->rpl_exp_len > rpl->hdr.rpl_len + rpl_hdr_len) ||
504 	    !rpl->hdr.rpl_chk_code) {
505 		err = -EIO;
506 		goto out;
507 	}
508 
509 	args->req.lpl_len = rpl->hdr.rpl_len;
510 	memcpy(args->req.payload, rpl->payload, args->req.lpl_len);
511 
512 out:
513 	kfree(page_data->data);
514 	return err;
515 }
516 
517 static int
518 __ethtool_cmis_cdb_execute_cmd(struct net_device *dev,
519 			       struct ethtool_module_eeprom *page_data,
520 			       u8 page, u32 offset, u32 length, void *data)
521 {
522 	const struct ethtool_ops *ops = dev->ethtool_ops;
523 	struct netlink_ext_ack extack = {};
524 	int err;
525 
526 	ethtool_cmis_page_init(page_data, page, offset, length);
527 	page_data->data = kmemdup(data, page_data->length, GFP_KERNEL);
528 	if (!page_data->data)
529 		return -ENOMEM;
530 
531 	err = ops->set_module_eeprom_by_page(dev, page_data, &extack);
532 	if (err < 0) {
533 		if (extack._msg)
534 			netdev_err(dev, "%s\n", extack._msg);
535 	}
536 
537 	kfree(page_data->data);
538 	return err;
539 }
540 
541 static u8 cmis_cdb_calc_checksum(const void *data, size_t size)
542 {
543 	const u8 *bytes = (const u8 *)data;
544 	u8 checksum = 0;
545 
546 	for (size_t i = 0; i < size; i++)
547 		checksum += bytes[i];
548 
549 	return ~checksum;
550 }
551 
552 #define CMIS_CDB_CMD_ID_OFFSET	0x80
553 
554 int ethtool_cmis_cdb_execute_cmd(struct net_device *dev,
555 				 struct ethtool_cmis_cdb_cmd_args *args)
556 {
557 	struct ethtool_module_eeprom page_data = {};
558 	u32 offset;
559 	int err;
560 
561 	args->req.chk_code =
562 		cmis_cdb_calc_checksum(&args->req, sizeof(args->req));
563 
564 	if (args->req.lpl_len > args->read_write_len_ext) {
565 		args->err_msg = "LPL length is longer than CDB read write length extension allows";
566 		return -EINVAL;
567 	}
568 
569 	/* According to the CMIS standard, there are two options to trigger the
570 	 * CDB commands. The default option is triggering the command by writing
571 	 * the CMDID bytes. Therefore, the command will be split to 2 calls:
572 	 * First, with everything except the CMDID field and then the CMDID
573 	 * field.
574 	 */
575 	offset = CMIS_CDB_CMD_ID_OFFSET +
576 		offsetof(struct ethtool_cmis_cdb_request, body);
577 	err = __ethtool_cmis_cdb_execute_cmd(dev, &page_data,
578 					     ETHTOOL_CMIS_CDB_CMD_PAGE, offset,
579 					     sizeof(args->req.body),
580 					     &args->req.body);
581 	if (err < 0)
582 		return err;
583 
584 	offset = CMIS_CDB_CMD_ID_OFFSET +
585 		offsetof(struct ethtool_cmis_cdb_request, id);
586 	err = __ethtool_cmis_cdb_execute_cmd(dev, &page_data,
587 					     ETHTOOL_CMIS_CDB_CMD_PAGE, offset,
588 					     sizeof(args->req.id),
589 					     &args->req.id);
590 	if (err < 0)
591 		return err;
592 
593 	err = cmis_cdb_wait_for_completion(dev, args);
594 	if (err < 0)
595 		return err;
596 
597 	err = cmis_cdb_wait_for_status(dev, args);
598 	if (err < 0)
599 		return err;
600 
601 	return cmis_cdb_process_reply(dev, &page_data, args);
602 }
603