cmis_cdb.c 15 KB

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