bootmeth_cros.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Bootmethod for ChromiumOS
  4. *
  5. * Copyright 2023 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 <bootdev.h>
  12. #include <bootflow.h>
  13. #include <bootmeth.h>
  14. #include <dm.h>
  15. #include <malloc.h>
  16. #include <mapmem.h>
  17. #include <part.h>
  18. #ifdef CONFIG_X86
  19. #include <asm/zimage.h>
  20. #endif
  21. #include <linux/sizes.h>
  22. enum {
  23. /* Offsets in the kernel-partition header */
  24. KERN_START = 0x4f0,
  25. KERN_SIZE = 0x518,
  26. SETUP_OFFSET = 0x1000, /* bytes before base */
  27. CMDLINE_OFFSET = 0x2000, /* bytes before base */
  28. OFFSET_BASE = 0x100000, /* assumed kernel load-address */
  29. };
  30. static int cros_check(struct udevice *dev, struct bootflow_iter *iter)
  31. {
  32. /* This only works on block and network devices */
  33. if (bootflow_iter_check_blk(iter))
  34. return log_msg_ret("blk", -ENOTSUPP);
  35. return 0;
  36. }
  37. static int copy_cmdline(const char *from, const char *uuid, char **bufp)
  38. {
  39. const int maxlen = 2048;
  40. char buf[maxlen];
  41. char *cmd, *to, *end;
  42. int len;
  43. /* Allow space for cmdline + UUID */
  44. len = strnlen(from, sizeof(buf));
  45. if (len >= maxlen)
  46. return -E2BIG;
  47. log_debug("uuid %d %s\n", uuid ? (int)strlen(uuid) : 0, uuid);
  48. for (to = buf, end = buf + maxlen - UUID_STR_LEN - 1; *from; from++) {
  49. if (to >= end)
  50. return -E2BIG;
  51. if (from[0] == '%' && from[1] == 'U' && uuid &&
  52. strlen(uuid) == UUID_STR_LEN) {
  53. strcpy(to, uuid);
  54. to += UUID_STR_LEN;
  55. from++;
  56. } else {
  57. *to++ = *from;
  58. }
  59. }
  60. *to = '\0';
  61. len = to - buf;
  62. cmd = strdup(buf);
  63. if (!cmd)
  64. return -ENOMEM;
  65. free(*bufp);
  66. *bufp = cmd;
  67. return 0;
  68. }
  69. static int cros_read_bootflow(struct udevice *dev, struct bootflow *bflow)
  70. {
  71. struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
  72. ulong base, start, size, setup, cmdline, num_blks, kern_base;
  73. struct disk_partition info;
  74. const char *uuid = NULL;
  75. void *buf, *hdr;
  76. int ret;
  77. log_debug("starting, part=%d\n", bflow->part);
  78. /* We consider the whole disk, not any one partition */
  79. if (bflow->part)
  80. return log_msg_ret("max", -ENOENT);
  81. /* Check partition 2 */
  82. ret = part_get_info(desc, 2, &info);
  83. if (ret)
  84. return log_msg_ret("part", ret);
  85. /* Make a buffer for the header information */
  86. num_blks = SZ_4K >> desc->log2blksz;
  87. log_debug("Reading header, blk=%s, start=%lx, blocks=%lx\n",
  88. bflow->blk->name, (ulong)info.start, num_blks);
  89. hdr = memalign(SZ_1K, SZ_4K);
  90. if (!hdr)
  91. return log_msg_ret("hdr", -ENOMEM);
  92. ret = blk_read(bflow->blk, info.start, num_blks, hdr);
  93. if (ret != num_blks)
  94. return log_msg_ret("inf", ret);
  95. if (memcmp("CHROMEOS", hdr, 8))
  96. return -ENOENT;
  97. log_info("Header at %lx\n", (ulong)map_to_sysmem(hdr));
  98. start = *(u32 *)(hdr + KERN_START);
  99. size = ALIGN(*(u32 *)(hdr + KERN_SIZE), desc->blksz);
  100. log_debug("Reading start %lx size %lx\n", start, size);
  101. bflow->size = size;
  102. buf = memalign(SZ_1K, size);
  103. if (!buf)
  104. return log_msg_ret("buf", -ENOMEM);
  105. num_blks = size >> desc->log2blksz;
  106. log_debug("Reading data, blk=%s, start=%lx, blocks=%lx\n",
  107. bflow->blk->name, (ulong)info.start, num_blks);
  108. ret = blk_read(bflow->blk, (ulong)info.start + 0x80, num_blks, buf);
  109. if (ret != num_blks)
  110. return log_msg_ret("inf", ret);
  111. base = map_to_sysmem(buf);
  112. setup = base + start - OFFSET_BASE - SETUP_OFFSET;
  113. cmdline = base + start - OFFSET_BASE - CMDLINE_OFFSET;
  114. kern_base = base + start - OFFSET_BASE + SZ_16K;
  115. log_debug("base %lx setup %lx, cmdline %lx, kern_base %lx\n", base,
  116. setup, cmdline, kern_base);
  117. #ifdef CONFIG_X86
  118. const char *version;
  119. version = zimage_get_kernel_version(map_sysmem(setup, 0),
  120. map_sysmem(kern_base, 0));
  121. log_debug("version %s\n", version);
  122. if (version)
  123. bflow->name = strdup(version);
  124. #endif
  125. if (!bflow->name)
  126. bflow->name = strdup("ChromeOS");
  127. if (!bflow->name)
  128. return log_msg_ret("nam", -ENOMEM);
  129. bflow->os_name = strdup("ChromeOS");
  130. if (!bflow->os_name)
  131. return log_msg_ret("os", -ENOMEM);
  132. #if CONFIG_IS_ENABLED(PARTITION_UUIDS)
  133. uuid = info.uuid;
  134. #endif
  135. ret = copy_cmdline(map_sysmem(cmdline, 0), uuid, &bflow->cmdline);
  136. if (ret)
  137. return log_msg_ret("cmd", ret);
  138. bflow->state = BOOTFLOWST_READY;
  139. bflow->buf = buf;
  140. bflow->x86_setup = map_sysmem(setup, 0);
  141. return 0;
  142. }
  143. static int cros_read_file(struct udevice *dev, struct bootflow *bflow,
  144. const char *file_path, ulong addr, ulong *sizep)
  145. {
  146. return -ENOSYS;
  147. }
  148. static int cros_boot(struct udevice *dev, struct bootflow *bflow)
  149. {
  150. #ifdef CONFIG_X86
  151. zboot_start(map_to_sysmem(bflow->buf), bflow->size, 0, 0,
  152. map_to_sysmem(bflow->x86_setup),
  153. bflow->cmdline);
  154. #endif
  155. return log_msg_ret("go", -EFAULT);
  156. }
  157. static int cros_bootmeth_bind(struct udevice *dev)
  158. {
  159. struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
  160. plat->desc = "ChromiumOS boot";
  161. return 0;
  162. }
  163. static struct bootmeth_ops cros_bootmeth_ops = {
  164. .check = cros_check,
  165. .read_bootflow = cros_read_bootflow,
  166. .read_file = cros_read_file,
  167. .boot = cros_boot,
  168. };
  169. static const struct udevice_id cros_bootmeth_ids[] = {
  170. { .compatible = "u-boot,cros" },
  171. { }
  172. };
  173. U_BOOT_DRIVER(bootmeth_cros) = {
  174. .name = "bootmeth_cros",
  175. .id = UCLASS_BOOTMETH,
  176. .of_match = cros_bootmeth_ids,
  177. .ops = &cros_bootmeth_ops,
  178. .bind = cros_bootmeth_bind,
  179. };