bootmeth_script.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Bootmethod for booting via a U-Boot script
  4. *
  5. * Copyright 2021 Google LLC
  6. * Written by Simon Glass <sjg@chromium.org>
  7. */
  8. #define LOG_CATEGORY UCLASS_BOOTSTD
  9. #include <common.h>
  10. #include <blk.h>
  11. #include <bootflow.h>
  12. #include <bootmeth.h>
  13. #include <bootstd.h>
  14. #include <dm.h>
  15. #include <env.h>
  16. #include <fs.h>
  17. #include <image.h>
  18. #include <malloc.h>
  19. #include <mapmem.h>
  20. #include <net.h>
  21. #define SCRIPT_FNAME1 "boot.scr.uimg"
  22. #define SCRIPT_FNAME2 "boot.scr"
  23. static int script_check(struct udevice *dev, struct bootflow_iter *iter)
  24. {
  25. /* This works on block devices, network devices and SPI Flash */
  26. if (iter->method_flags & BOOTFLOW_METHF_PXE_ONLY)
  27. return log_msg_ret("pxe", -ENOTSUPP);
  28. return 0;
  29. }
  30. /**
  31. * script_fill_info() - Decode the U-Boot script to find out distro info
  32. *
  33. * @bflow: Bootflow to process
  34. * @return 0 if OK, -ve on error
  35. */
  36. static int script_fill_info(struct bootflow *bflow)
  37. {
  38. char *name = NULL;
  39. char *data;
  40. uint len;
  41. int ret;
  42. log_debug("parsing bflow file size %x\n", bflow->size);
  43. ret = image_locate_script(bflow->buf, bflow->size, NULL, NULL, &data, &len);
  44. if (!ret) {
  45. if (strstr(data, "armbianEnv"))
  46. name = "Armbian";
  47. }
  48. if (name) {
  49. bflow->os_name = strdup(name);
  50. if (!bflow->os_name)
  51. return log_msg_ret("os", -ENOMEM);
  52. }
  53. return 0;
  54. }
  55. static int script_read_bootflow_file(struct udevice *bootstd,
  56. struct bootflow *bflow)
  57. {
  58. struct blk_desc *desc = NULL;
  59. const char *const *prefixes;
  60. const char *prefix;
  61. int ret, i;
  62. ret = uclass_first_device_err(UCLASS_BOOTSTD, &bootstd);
  63. if (ret)
  64. return log_msg_ret("std", ret);
  65. if (bflow->blk) {
  66. /* We require a partition table */
  67. if (!bflow->part)
  68. return -ENOENT;
  69. desc = dev_get_uclass_plat(bflow->blk);
  70. }
  71. prefixes = bootstd_get_prefixes(bootstd);
  72. i = 0;
  73. do {
  74. prefix = prefixes ? prefixes[i] : NULL;
  75. ret = bootmeth_try_file(bflow, desc, prefix, SCRIPT_FNAME1);
  76. if (ret)
  77. ret = bootmeth_try_file(bflow, desc, prefix,
  78. SCRIPT_FNAME2);
  79. } while (ret && prefixes && prefixes[++i]);
  80. if (ret)
  81. return log_msg_ret("try", ret);
  82. bflow->subdir = strdup(prefix ? prefix : "");
  83. if (!bflow->subdir)
  84. return log_msg_ret("prefix", -ENOMEM);
  85. ret = bootmeth_alloc_file(bflow, 0x10000, 1);
  86. if (ret)
  87. return log_msg_ret("read", ret);
  88. ret = script_fill_info(bflow);
  89. if (ret)
  90. return log_msg_ret("inf", ret);
  91. ret = bootmeth_alloc_other(bflow, "boot.bmp", &bflow->logo,
  92. &bflow->logo_size);
  93. /* ignore error */
  94. return 0;
  95. }
  96. static int script_read_bootflow_net(struct bootflow *bflow)
  97. {
  98. const char *addr_str;
  99. int size, ret;
  100. char *fname;
  101. ulong addr;
  102. /* figure out the load address */
  103. addr_str = env_get("scriptaddr");
  104. addr = addr_str ? hextoul(addr_str, NULL) : image_load_addr;
  105. fname = env_get("boot_script_dhcp");
  106. if (!fname)
  107. return log_msg_ret("dhc", -EINVAL);
  108. ret = dhcp_run(addr, fname, true);
  109. if (ret)
  110. return log_msg_ret("dhc", ret);
  111. size = env_get_hex("filesize", 0);
  112. if (!size)
  113. return log_msg_ret("sz", -EINVAL);
  114. bflow->buf = malloc(size + 1);
  115. if (!bflow->buf)
  116. return log_msg_ret("buf", -ENOMEM);
  117. memcpy(bflow->buf, map_sysmem(addr, size), size);
  118. bflow->buf[size] = '\0';
  119. bflow->size = size;
  120. bflow->state = BOOTFLOWST_READY;
  121. return 0;
  122. }
  123. static int script_read_bootflow(struct udevice *dev, struct bootflow *bflow)
  124. {
  125. const struct udevice *media = dev_get_parent(bflow->dev);
  126. struct udevice *bootstd;
  127. int ret;
  128. ret = uclass_first_device_err(UCLASS_BOOTSTD, &bootstd);
  129. if (ret)
  130. return log_msg_ret("std", ret);
  131. if (IS_ENABLED(CONFIG_CMD_DHCP) &&
  132. device_get_uclass_id(media) == UCLASS_ETH) {
  133. /* we only support reading from one device, so ignore 'dev' */
  134. ret = script_read_bootflow_net(bflow);
  135. if (ret)
  136. return log_msg_ret("net", ret);
  137. } else {
  138. ret = script_read_bootflow_file(bootstd, bflow);
  139. if (ret)
  140. return log_msg_ret("blk", ret);
  141. }
  142. return 0;
  143. }
  144. static int script_set_bootflow(struct udevice *dev, struct bootflow *bflow,
  145. char *buf, int size)
  146. {
  147. buf[size] = '\0';
  148. bflow->buf = buf;
  149. bflow->size = size;
  150. bflow->state = BOOTFLOWST_READY;
  151. return 0;
  152. }
  153. static int script_boot(struct udevice *dev, struct bootflow *bflow)
  154. {
  155. struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
  156. ulong addr;
  157. int ret;
  158. if (desc->uclass_id == UCLASS_USB)
  159. ret = env_set("devtype", "usb");
  160. else
  161. ret = env_set("devtype", blk_get_devtype(bflow->blk));
  162. if (!ret)
  163. ret = env_set_hex("devnum", desc->devnum);
  164. if (!ret)
  165. ret = env_set_hex("distro_bootpart", bflow->part);
  166. if (!ret)
  167. ret = env_set("prefix", bflow->subdir);
  168. if (!ret && IS_ENABLED(CONFIG_ARCH_SUNXI) &&
  169. !strcmp("mmc", blk_get_devtype(bflow->blk)))
  170. ret = env_set_hex("mmc_bootdev", desc->devnum);
  171. if (ret)
  172. return log_msg_ret("env", ret);
  173. log_debug("devtype: %s\n", env_get("devtype"));
  174. log_debug("devnum: %s\n", env_get("devnum"));
  175. log_debug("distro_bootpart: %s\n", env_get("distro_bootpart"));
  176. log_debug("prefix: %s\n", env_get("prefix"));
  177. log_debug("mmc_bootdev: %s\n", env_get("mmc_bootdev"));
  178. addr = map_to_sysmem(bflow->buf);
  179. ret = cmd_source_script(addr, NULL, NULL);
  180. if (ret)
  181. return log_msg_ret("boot", ret);
  182. return 0;
  183. }
  184. static int script_bootmeth_bind(struct udevice *dev)
  185. {
  186. struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
  187. plat->desc = IS_ENABLED(CONFIG_BOOTSTD_FULL) ?
  188. "Script boot from a block device" : "script";
  189. return 0;
  190. }
  191. static struct bootmeth_ops script_bootmeth_ops = {
  192. .check = script_check,
  193. .read_bootflow = script_read_bootflow,
  194. .set_bootflow = script_set_bootflow,
  195. .read_file = bootmeth_common_read_file,
  196. .boot = script_boot,
  197. };
  198. static const struct udevice_id script_bootmeth_ids[] = {
  199. { .compatible = "u-boot,script" },
  200. { }
  201. };
  202. /* Put an number before 'script' to provide a default ordering */
  203. U_BOOT_DRIVER(bootmeth_2script) = {
  204. .name = "bootmeth_script",
  205. .id = UCLASS_BOOTMETH,
  206. .of_match = script_bootmeth_ids,
  207. .ops = &script_bootmeth_ops,
  208. .bind = script_bootmeth_bind,
  209. };