bootmeth_extlinux.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Bootmethod for extlinux boot from a block device
  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 <bootdev.h>
  11. #include <bootflow.h>
  12. #include <bootmeth.h>
  13. #include <bootstd.h>
  14. #include <command.h>
  15. #include <dm.h>
  16. #include <extlinux.h>
  17. #include <fs.h>
  18. #include <malloc.h>
  19. #include <mapmem.h>
  20. #include <mmc.h>
  21. #include <pxe_utils.h>
  22. static int extlinux_get_state_desc(struct udevice *dev, char *buf, int maxsize)
  23. {
  24. if (IS_ENABLED(CONFIG_SANDBOX)) {
  25. int len;
  26. len = snprintf(buf, maxsize, "OK");
  27. return len + 1 < maxsize ? 0 : -ENOSPC;
  28. }
  29. return 0;
  30. }
  31. static int extlinux_getfile(struct pxe_context *ctx, const char *file_path,
  32. char *file_addr, ulong *sizep)
  33. {
  34. struct extlinux_info *info = ctx->userdata;
  35. ulong addr;
  36. int ret;
  37. addr = simple_strtoul(file_addr, NULL, 16);
  38. /* Allow up to 1GB */
  39. *sizep = 1 << 30;
  40. ret = bootmeth_read_file(info->dev, info->bflow, file_path, addr,
  41. sizep);
  42. if (ret)
  43. return log_msg_ret("read", ret);
  44. return 0;
  45. }
  46. static int extlinux_check(struct udevice *dev, struct bootflow_iter *iter)
  47. {
  48. int ret;
  49. /* This only works on block devices */
  50. ret = bootflow_iter_check_blk(iter);
  51. if (ret)
  52. return log_msg_ret("blk", ret);
  53. return 0;
  54. }
  55. /**
  56. * extlinux_fill_info() - Decode the extlinux file to find out its info
  57. *
  58. * @bflow: Bootflow to process
  59. * @return 0 if OK, -ve on error
  60. */
  61. static int extlinux_fill_info(struct bootflow *bflow)
  62. {
  63. struct membuff mb;
  64. char line[200];
  65. char *data;
  66. int len;
  67. log_debug("parsing bflow file size %x\n", bflow->size);
  68. membuff_init(&mb, bflow->buf, bflow->size);
  69. membuff_putraw(&mb, bflow->size, true, &data);
  70. while (len = membuff_readline(&mb, line, sizeof(line) - 1, ' '), len) {
  71. char *tok, *p = line;
  72. tok = strsep(&p, " ");
  73. if (p) {
  74. if (!strcmp("label", tok)) {
  75. bflow->os_name = strdup(p);
  76. if (!bflow->os_name)
  77. return log_msg_ret("os", -ENOMEM);
  78. }
  79. }
  80. }
  81. return 0;
  82. }
  83. static int extlinux_read_bootflow(struct udevice *dev, struct bootflow *bflow)
  84. {
  85. struct blk_desc *desc;
  86. const char *const *prefixes;
  87. struct udevice *bootstd;
  88. const char *prefix;
  89. loff_t size;
  90. int ret, i;
  91. ret = uclass_first_device_err(UCLASS_BOOTSTD, &bootstd);
  92. if (ret)
  93. return log_msg_ret("std", ret);
  94. /* If a block device, we require a partition table */
  95. if (bflow->blk && !bflow->part)
  96. return -ENOENT;
  97. prefixes = bootstd_get_prefixes(bootstd);
  98. i = 0;
  99. desc = bflow->blk ? dev_get_uclass_plat(bflow->blk) : NULL;
  100. do {
  101. prefix = prefixes ? prefixes[i] : NULL;
  102. ret = bootmeth_try_file(bflow, desc, prefix, EXTLINUX_FNAME);
  103. } while (ret && prefixes && prefixes[++i]);
  104. if (ret)
  105. return log_msg_ret("try", ret);
  106. size = bflow->size;
  107. ret = bootmeth_alloc_file(bflow, 0x10000, 1);
  108. if (ret)
  109. return log_msg_ret("read", ret);
  110. ret = extlinux_fill_info(bflow);
  111. if (ret)
  112. return log_msg_ret("inf", ret);
  113. return 0;
  114. }
  115. static int extlinux_boot(struct udevice *dev, struct bootflow *bflow)
  116. {
  117. struct cmd_tbl cmdtp = {}; /* dummy */
  118. struct pxe_context ctx;
  119. struct extlinux_info info;
  120. ulong addr;
  121. int ret;
  122. addr = map_to_sysmem(bflow->buf);
  123. info.dev = dev;
  124. info.bflow = bflow;
  125. ret = pxe_setup_ctx(&ctx, &cmdtp, extlinux_getfile, &info, true,
  126. bflow->fname, false);
  127. if (ret)
  128. return log_msg_ret("ctx", -EINVAL);
  129. ret = pxe_process(&ctx, addr, false);
  130. if (ret)
  131. return log_msg_ret("bread", -EINVAL);
  132. return 0;
  133. }
  134. static int extlinux_bootmeth_bind(struct udevice *dev)
  135. {
  136. struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
  137. plat->desc = IS_ENABLED(CONFIG_BOOTSTD_FULL) ?
  138. "Extlinux boot from a block device" : "extlinux";
  139. return 0;
  140. }
  141. static struct bootmeth_ops extlinux_bootmeth_ops = {
  142. .get_state_desc = extlinux_get_state_desc,
  143. .check = extlinux_check,
  144. .read_bootflow = extlinux_read_bootflow,
  145. .read_file = bootmeth_common_read_file,
  146. .boot = extlinux_boot,
  147. };
  148. static const struct udevice_id extlinux_bootmeth_ids[] = {
  149. { .compatible = "u-boot,extlinux" },
  150. { }
  151. };
  152. /* Put an number before 'extlinux' to provide a default ordering */
  153. U_BOOT_DRIVER(bootmeth_1extlinux) = {
  154. .name = "bootmeth_extlinux",
  155. .id = UCLASS_BOOTMETH,
  156. .of_match = extlinux_bootmeth_ids,
  157. .ops = &extlinux_bootmeth_ops,
  158. .bind = extlinux_bootmeth_bind,
  159. };