tdx-cfg-block.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. /*
  2. * Copyright (c) 2016 Toradex, Inc.
  3. *
  4. * SPDX-License-Identifier: GPL-2.0+
  5. */
  6. #include <common.h>
  7. #include "tdx-cfg-block.h"
  8. #if defined(CONFIG_TARGET_APALIS_IMX6) || defined(CONFIG_TARGET_COLIBRI_IMX6)
  9. #include <asm/arch/sys_proto.h>
  10. #else
  11. #define is_cpu_type(cpu) (0)
  12. #endif
  13. #if defined(CONFIG_CPU_PXA27X)
  14. #include <asm/arch-pxa/pxa.h>
  15. #else
  16. #define cpu_is_pxa27x(cpu) (0)
  17. #endif
  18. #include <cli.h>
  19. #include <console.h>
  20. #include <flash.h>
  21. #include <malloc.h>
  22. #include <mmc.h>
  23. #include <nand.h>
  24. DECLARE_GLOBAL_DATA_PTR;
  25. #define TAG_VALID 0xcf01
  26. #define TAG_MAC 0x0000
  27. #define TAG_HW 0x0008
  28. #define TAG_INVALID 0xffff
  29. #define TAG_FLAG_VALID 0x1
  30. #if defined(CONFIG_TDX_CFG_BLOCK_IS_IN_MMC)
  31. #define TDX_CFG_BLOCK_MAX_SIZE 512
  32. #elif defined(CONFIG_TDX_CFG_BLOCK_IS_IN_NAND)
  33. #define TDX_CFG_BLOCK_MAX_SIZE 64
  34. #elif defined(CONFIG_TDX_CFG_BLOCK_IS_IN_NOR)
  35. #define TDX_CFG_BLOCK_MAX_SIZE 64
  36. #else
  37. #error Toradex config block location not set
  38. #endif
  39. struct toradex_tag {
  40. u32 len:14;
  41. u32 flags:2;
  42. u32 id:16;
  43. };
  44. bool valid_cfgblock;
  45. struct toradex_hw tdx_hw_tag;
  46. struct toradex_eth_addr tdx_eth_addr;
  47. u32 tdx_serial;
  48. const char * const toradex_modules[] = {
  49. [0] = "UNKNOWN MODULE",
  50. [1] = "Colibri PXA270 312MHz",
  51. [2] = "Colibri PXA270 520MHz",
  52. [3] = "Colibri PXA320 806MHz",
  53. [4] = "Colibri PXA300 208MHz",
  54. [5] = "Colibri PXA310 624MHz",
  55. [6] = "Colibri PXA320 806MHz IT",
  56. [7] = "Colibri PXA300 208MHz XT",
  57. [8] = "Colibri PXA270 312MHz",
  58. [9] = "Colibri PXA270 520MHz",
  59. [10] = "Colibri VF50 128MB", /* not currently on sale */
  60. [11] = "Colibri VF61 256MB",
  61. [12] = "Colibri VF61 256MB IT",
  62. [13] = "Colibri VF50 128MB IT",
  63. [14] = "Colibri iMX6 Solo 256MB",
  64. [15] = "Colibri iMX6 DualLite 512MB",
  65. [16] = "Colibri iMX6 Solo 256MB IT",
  66. [17] = "Colibri iMX6 DualLite 512MB IT",
  67. [18] = "UNKNOWN MODULE",
  68. [19] = "UNKNOWN MODULE",
  69. [20] = "Colibri T20 256MB",
  70. [21] = "Colibri T20 512MB",
  71. [22] = "Colibri T20 512MB IT",
  72. [23] = "Colibri T30 1GB",
  73. [24] = "Colibri T20 256MB IT",
  74. [25] = "Apalis T30 2GB",
  75. [26] = "Apalis T30 1GB",
  76. [27] = "Apalis iMX6 Quad 1GB",
  77. [28] = "Apalis iMX6 Quad 2GB IT",
  78. [29] = "Apalis iMX6 Dual 512MB",
  79. [30] = "Colibri T30 1GB IT",
  80. [31] = "Apalis T30 1GB IT",
  81. [32] = "Colibri iMX7 Solo 256MB",
  82. [33] = "Colibri iMX7 Dual 512MB",
  83. [34] = "Apalis TK1 2GB",
  84. [35] = "Apalis iMX6 Dual 1GB IT",
  85. };
  86. #ifdef CONFIG_TDX_CFG_BLOCK_IS_IN_MMC
  87. static int tdx_cfg_block_mmc_storage(u8 *config_block, int write)
  88. {
  89. struct mmc *mmc;
  90. int dev = CONFIG_TDX_CFG_BLOCK_DEV;
  91. int offset = CONFIG_TDX_CFG_BLOCK_OFFSET;
  92. uint part = CONFIG_TDX_CFG_BLOCK_PART;
  93. uint blk_start;
  94. int ret = 0;
  95. /* Read production parameter config block from eMMC */
  96. mmc = find_mmc_device(dev);
  97. if (!mmc) {
  98. puts("No MMC card found\n");
  99. ret = -ENODEV;
  100. goto out;
  101. }
  102. if (part != mmc->block_dev.hwpart) {
  103. if (blk_select_hwpart_devnum(IF_TYPE_MMC, dev, part)) {
  104. puts("MMC partition switch failed\n");
  105. ret = -ENODEV;
  106. goto out;
  107. }
  108. }
  109. if (offset < 0)
  110. offset += mmc->capacity;
  111. blk_start = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len;
  112. if (!write) {
  113. /* Careful reads a whole block of 512 bytes into config_block */
  114. if (blk_dread(mmc_get_blk_desc(mmc), blk_start, 1,
  115. (unsigned char *)config_block) != 1) {
  116. ret = -EIO;
  117. goto out;
  118. }
  119. /* Flush cache after read */
  120. flush_cache((ulong)(unsigned char *)config_block, 512);
  121. } else {
  122. /* Just writing one 512 byte block */
  123. if (blk_dwrite(mmc_get_blk_desc(mmc), blk_start, 1,
  124. (unsigned char *)config_block) != 1) {
  125. ret = -EIO;
  126. goto out;
  127. }
  128. }
  129. out:
  130. /* Switch back to regular eMMC user partition */
  131. blk_select_hwpart_devnum(IF_TYPE_MMC, 0, 0);
  132. return ret;
  133. }
  134. #endif
  135. #ifdef CONFIG_TDX_CFG_BLOCK_IS_IN_NAND
  136. static int read_tdx_cfg_block_from_nand(unsigned char *config_block)
  137. {
  138. size_t size = TDX_CFG_BLOCK_MAX_SIZE;
  139. /* Read production parameter config block from NAND page */
  140. return nand_read_skip_bad(nand_info[0], CONFIG_TDX_CFG_BLOCK_OFFSET,
  141. &size, NULL, TDX_CFG_BLOCK_MAX_SIZE, config_block);
  142. }
  143. static int write_tdx_cfg_block_to_nand(unsigned char *config_block)
  144. {
  145. size_t size = TDX_CFG_BLOCK_MAX_SIZE;
  146. /* Write production parameter config block to NAND page */
  147. return nand_write_skip_bad(nand_info[0], CONFIG_TDX_CFG_BLOCK_OFFSET,
  148. &size, NULL, TDX_CFG_BLOCK_MAX_SIZE,
  149. config_block, WITH_WR_VERIFY);
  150. }
  151. #endif
  152. #ifdef CONFIG_TDX_CFG_BLOCK_IS_IN_NOR
  153. static int read_tdx_cfg_block_from_nor(unsigned char *config_block)
  154. {
  155. /* Read production parameter config block from NOR flash */
  156. memcpy(config_block, (void *)CONFIG_TDX_CFG_BLOCK_OFFSET,
  157. TDX_CFG_BLOCK_MAX_SIZE);
  158. return 0;
  159. }
  160. static int write_tdx_cfg_block_to_nor(unsigned char *config_block)
  161. {
  162. /* Write production parameter config block to NOR flash */
  163. return flash_write((void *)config_block, CONFIG_TDX_CFG_BLOCK_OFFSET,
  164. TDX_CFG_BLOCK_MAX_SIZE);
  165. }
  166. #endif
  167. int read_tdx_cfg_block(void)
  168. {
  169. int ret = 0;
  170. u8 *config_block = NULL;
  171. struct toradex_tag *tag;
  172. size_t size = TDX_CFG_BLOCK_MAX_SIZE;
  173. int offset;
  174. /* Allocate RAM area for config block */
  175. config_block = memalign(ARCH_DMA_MINALIGN, size);
  176. if (!config_block) {
  177. printf("Not enough malloc space available!\n");
  178. return -ENOMEM;
  179. }
  180. memset(config_block, 0, size);
  181. #if defined(CONFIG_TDX_CFG_BLOCK_IS_IN_MMC)
  182. ret = tdx_cfg_block_mmc_storage(config_block, 0);
  183. #elif defined(CONFIG_TDX_CFG_BLOCK_IS_IN_NAND)
  184. ret = read_tdx_cfg_block_from_nand(config_block);
  185. #elif defined(CONFIG_TDX_CFG_BLOCK_IS_IN_NOR)
  186. ret = read_tdx_cfg_block_from_nor(config_block);
  187. #else
  188. ret = -EINVAL;
  189. #endif
  190. if (ret)
  191. goto out;
  192. /* Expect a valid tag first */
  193. tag = (struct toradex_tag *)config_block;
  194. if (tag->flags != TAG_FLAG_VALID || tag->id != TAG_VALID) {
  195. valid_cfgblock = false;
  196. ret = -EINVAL;
  197. goto out;
  198. }
  199. valid_cfgblock = true;
  200. offset = 4;
  201. while (offset < TDX_CFG_BLOCK_MAX_SIZE) {
  202. tag = (struct toradex_tag *)(config_block + offset);
  203. offset += 4;
  204. if (tag->id == TAG_INVALID)
  205. break;
  206. if (tag->flags == TAG_FLAG_VALID) {
  207. switch (tag->id) {
  208. case TAG_MAC:
  209. memcpy(&tdx_eth_addr, config_block + offset,
  210. 6);
  211. /* NIC part of MAC address is serial number */
  212. tdx_serial = ntohl(tdx_eth_addr.nic) >> 8;
  213. break;
  214. case TAG_HW:
  215. memcpy(&tdx_hw_tag, config_block + offset, 8);
  216. break;
  217. }
  218. }
  219. /* Get to next tag according to current tags length */
  220. offset += tag->len * 4;
  221. }
  222. /* Cap product id to avoid issues with a yet unknown one */
  223. if (tdx_hw_tag.prodid > (sizeof(toradex_modules) /
  224. sizeof(toradex_modules[0])))
  225. tdx_hw_tag.prodid = 0;
  226. out:
  227. free(config_block);
  228. return ret;
  229. }
  230. static int get_cfgblock_interactive(void)
  231. {
  232. char message[CONFIG_SYS_CBSIZE];
  233. char *soc;
  234. char it = 'n';
  235. int len;
  236. if (cpu_is_pxa27x())
  237. sprintf(message, "Is the module the 312 MHz version? [y/N] ");
  238. else
  239. sprintf(message, "Is the module an IT version? [y/N] ");
  240. len = cli_readline(message);
  241. it = console_buffer[0];
  242. soc = getenv("soc");
  243. if (!strcmp("mx6", soc)) {
  244. #ifdef CONFIG_MACH_TYPE
  245. if (it == 'y' || it == 'Y')
  246. if (is_cpu_type(MXC_CPU_MX6Q))
  247. tdx_hw_tag.prodid = APALIS_IMX6Q_IT;
  248. else
  249. tdx_hw_tag.prodid = APALIS_IMX6D_IT;
  250. else
  251. if (is_cpu_type(MXC_CPU_MX6Q))
  252. tdx_hw_tag.prodid = APALIS_IMX6Q;
  253. else
  254. tdx_hw_tag.prodid = APALIS_IMX6D;
  255. #else
  256. if (it == 'y' || it == 'Y')
  257. if (is_cpu_type(MXC_CPU_MX6DL))
  258. tdx_hw_tag.prodid = COLIBRI_IMX6DL_IT;
  259. else
  260. tdx_hw_tag.prodid = COLIBRI_IMX6S_IT;
  261. else
  262. if (is_cpu_type(MXC_CPU_MX6DL))
  263. tdx_hw_tag.prodid = COLIBRI_IMX6DL;
  264. else
  265. tdx_hw_tag.prodid = COLIBRI_IMX6S;
  266. #endif /* CONFIG_MACH_TYPE */
  267. } else if (!strcmp("imx7d", soc)) {
  268. tdx_hw_tag.prodid = COLIBRI_IMX7D;
  269. } else if (!strcmp("imx7s", soc)) {
  270. tdx_hw_tag.prodid = COLIBRI_IMX7S;
  271. } else if (!strcmp("tegra20", soc)) {
  272. if (it == 'y' || it == 'Y')
  273. if (gd->ram_size == 0x10000000)
  274. tdx_hw_tag.prodid = COLIBRI_T20_256MB_IT;
  275. else
  276. tdx_hw_tag.prodid = COLIBRI_T20_512MB_IT;
  277. else
  278. if (gd->ram_size == 0x10000000)
  279. tdx_hw_tag.prodid = COLIBRI_T20_256MB;
  280. else
  281. tdx_hw_tag.prodid = COLIBRI_T20_512MB;
  282. } else if (cpu_is_pxa27x()) {
  283. if (it == 'y' || it == 'Y')
  284. tdx_hw_tag.prodid = COLIBRI_PXA270_312MHZ;
  285. else
  286. tdx_hw_tag.prodid = COLIBRI_PXA270_520MHZ;
  287. #ifdef CONFIG_MACH_TYPE
  288. } else if (!strcmp("tegra30", soc)) {
  289. if (CONFIG_MACH_TYPE == MACH_TYPE_APALIS_T30) {
  290. if (it == 'y' || it == 'Y')
  291. tdx_hw_tag.prodid = APALIS_T30_IT;
  292. else
  293. if (gd->ram_size == 0x40000000)
  294. tdx_hw_tag.prodid = APALIS_T30_1GB;
  295. else
  296. tdx_hw_tag.prodid = APALIS_T30_2GB;
  297. } else {
  298. if (it == 'y' || it == 'Y')
  299. tdx_hw_tag.prodid = COLIBRI_T30_IT;
  300. else
  301. tdx_hw_tag.prodid = COLIBRI_T30;
  302. }
  303. #endif /* CONFIG_MACH_TYPE */
  304. } else if (!strcmp("tegra124", soc)) {
  305. tdx_hw_tag.prodid = APALIS_TK1_2GB;
  306. } else if (!strcmp("vf500", soc)) {
  307. if (it == 'y' || it == 'Y')
  308. tdx_hw_tag.prodid = COLIBRI_VF50_IT;
  309. else
  310. tdx_hw_tag.prodid = COLIBRI_VF50;
  311. } else if (!strcmp("vf610", soc)) {
  312. if (it == 'y' || it == 'Y')
  313. tdx_hw_tag.prodid = COLIBRI_VF61_IT;
  314. else
  315. tdx_hw_tag.prodid = COLIBRI_VF61;
  316. } else {
  317. printf("Module type not detectable due to unknown SoC\n");
  318. return -1;
  319. }
  320. while (len < 4) {
  321. sprintf(message, "Enter the module version (e.g. V1.1B): V");
  322. len = cli_readline(message);
  323. }
  324. tdx_hw_tag.ver_major = console_buffer[0] - '0';
  325. tdx_hw_tag.ver_minor = console_buffer[2] - '0';
  326. tdx_hw_tag.ver_assembly = console_buffer[3] - 'A';
  327. if (cpu_is_pxa27x() && (tdx_hw_tag.ver_major == 1))
  328. tdx_hw_tag.prodid -= (COLIBRI_PXA270_312MHZ -
  329. COLIBRI_PXA270_V1_312MHZ);
  330. while (len < 8) {
  331. sprintf(message, "Enter module serial number: ");
  332. len = cli_readline(message);
  333. }
  334. tdx_serial = simple_strtoul(console_buffer, NULL, 10);
  335. return 0;
  336. }
  337. static int get_cfgblock_barcode(char *barcode)
  338. {
  339. if (strlen(barcode) < 16) {
  340. printf("Argument too short, barcode is 16 chars long\n");
  341. return -1;
  342. }
  343. /* Get hardware information from the first 8 digits */
  344. tdx_hw_tag.ver_major = barcode[4] - '0';
  345. tdx_hw_tag.ver_minor = barcode[5] - '0';
  346. tdx_hw_tag.ver_assembly = barcode[7] - '0';
  347. barcode[4] = '\0';
  348. tdx_hw_tag.prodid = simple_strtoul(barcode, NULL, 10);
  349. /* Parse second part of the barcode (serial number */
  350. barcode += 8;
  351. tdx_serial = simple_strtoul(barcode, NULL, 10);
  352. return 0;
  353. }
  354. static int do_cfgblock_create(cmd_tbl_t *cmdtp, int flag, int argc,
  355. char * const argv[])
  356. {
  357. u8 *config_block;
  358. struct toradex_tag *tag;
  359. size_t size = TDX_CFG_BLOCK_MAX_SIZE;
  360. int offset = 0;
  361. int ret = CMD_RET_SUCCESS;
  362. int err;
  363. /* Allocate RAM area for config block */
  364. config_block = memalign(ARCH_DMA_MINALIGN, size);
  365. if (!config_block) {
  366. printf("Not enough malloc space available!\n");
  367. return CMD_RET_FAILURE;
  368. }
  369. memset(config_block, 0xff, size);
  370. read_tdx_cfg_block();
  371. if (valid_cfgblock) {
  372. #if defined(CONFIG_TDX_CFG_BLOCK_IS_IN_NAND)
  373. /*
  374. * On NAND devices, recreation is only allowed if the page is
  375. * empty (config block invalid...)
  376. */
  377. printf("NAND erase block %d need to be erased before creating a Toradex config block\n",
  378. CONFIG_TDX_CFG_BLOCK_OFFSET / nand_info[0]->erasesize);
  379. goto out;
  380. #elif defined(CONFIG_TDX_CFG_BLOCK_IS_IN_NOR)
  381. /*
  382. * On NOR devices, recreation is only allowed if the sector is
  383. * empty and write protection is off (config block invalid...)
  384. */
  385. printf("NOR sector at offset 0x%02x need to be erased and unprotected before creating a Toradex config block\n",
  386. CONFIG_TDX_CFG_BLOCK_OFFSET);
  387. goto out;
  388. #else
  389. char message[CONFIG_SYS_CBSIZE];
  390. sprintf(message,
  391. "A valid Toradex config block is present, still recreate? [y/N] ");
  392. if (!cli_readline(message))
  393. goto out;
  394. if (console_buffer[0] != 'y' && console_buffer[0] != 'Y')
  395. goto out;
  396. #endif
  397. }
  398. /* Parse new Toradex config block data... */
  399. if (argc < 3)
  400. err = get_cfgblock_interactive();
  401. else
  402. err = get_cfgblock_barcode(argv[2]);
  403. if (err) {
  404. ret = CMD_RET_FAILURE;
  405. goto out;
  406. }
  407. /* Convert serial number to MAC address (the storage format) */
  408. tdx_eth_addr.oui = htonl(0x00142dUL << 8);
  409. tdx_eth_addr.nic = htonl(tdx_serial << 8);
  410. /* Valid Tag */
  411. tag = (struct toradex_tag *)config_block;
  412. tag->id = TAG_VALID;
  413. tag->flags = TAG_FLAG_VALID;
  414. tag->len = 0;
  415. offset += 4;
  416. /* Product Tag */
  417. tag = (struct toradex_tag *)(config_block + offset);
  418. tag->id = TAG_HW;
  419. tag->flags = TAG_FLAG_VALID;
  420. tag->len = 2;
  421. offset += 4;
  422. memcpy(config_block + offset, &tdx_hw_tag, 8);
  423. offset += 8;
  424. /* MAC Tag */
  425. tag = (struct toradex_tag *)(config_block + offset);
  426. tag->id = TAG_MAC;
  427. tag->flags = TAG_FLAG_VALID;
  428. tag->len = 2;
  429. offset += 4;
  430. memcpy(config_block + offset, &tdx_eth_addr, 6);
  431. offset += 6;
  432. memset(config_block + offset, 0, 32 - offset);
  433. #if defined(CONFIG_TDX_CFG_BLOCK_IS_IN_MMC)
  434. err = tdx_cfg_block_mmc_storage(config_block, 1);
  435. #elif defined(CONFIG_TDX_CFG_BLOCK_IS_IN_NAND)
  436. err = write_tdx_cfg_block_to_nand(config_block);
  437. #elif defined(CONFIG_TDX_CFG_BLOCK_IS_IN_NOR)
  438. err = write_tdx_cfg_block_to_nor(config_block);
  439. #else
  440. err = -EINVAL;
  441. #endif
  442. if (err) {
  443. printf("Failed to write Toradex config block: %d\n", ret);
  444. ret = CMD_RET_FAILURE;
  445. goto out;
  446. }
  447. printf("Toradex config block successfully written\n");
  448. out:
  449. free(config_block);
  450. return ret;
  451. }
  452. static int do_cfgblock(cmd_tbl_t *cmdtp, int flag, int argc,
  453. char * const argv[])
  454. {
  455. int ret;
  456. if (argc < 2)
  457. return CMD_RET_USAGE;
  458. if (!strcmp(argv[1], "create")) {
  459. return do_cfgblock_create(cmdtp, flag, argc, argv);
  460. } else if (!strcmp(argv[1], "reload")) {
  461. ret = read_tdx_cfg_block();
  462. if (ret) {
  463. printf("Failed to reload Toradex config block: %d\n",
  464. ret);
  465. return CMD_RET_FAILURE;
  466. }
  467. return CMD_RET_SUCCESS;
  468. }
  469. return CMD_RET_USAGE;
  470. }
  471. U_BOOT_CMD(
  472. cfgblock, 3, 0, do_cfgblock,
  473. "Toradex config block handling commands",
  474. "create [barcode] - (Re-)create Toradex config block\n"
  475. "cfgblock reload - Reload Toradex config block from flash"
  476. );