1 // SPDX-License-Identifier: GPL-2.0+
3 * Bootmethod for ChromiumOS
5 * Copyright 2023 Google LLC
9 #define LOG_CATEGORY UCLASS_BOOTSTD
16 #include <display_options.h>
22 #include <linux/sizes.h>
23 #include "bootmeth_cros.h"
25 static const efi_guid_t cros_kern_type = PARTITION_CROS_KERNEL;
28 * Layout of the ChromeOS kernel
30 * Partitions 2 and 4 contain kernels with type GUID_CROS_KERNEL
35 * 0 struct vb2_keyblock
36 * m struct vb2_kernel_preamble
39 * m is keyblock->keyblock_size
40 * n is preamble->preamble_size
42 * The kernel buffer itself consists of various parts:
45 * m + n kernel image (Flat vmlinux binary or FIT)
46 * b - 8KB Command line text
47 * b - 4KB X86 setup block (struct boot_params, extends for about 16KB)
48 * b X86 bootloader (continuation of setup block)
49 * b + 16KB X86 setup block (copy, used for hold data pointed to)
51 * b is m + n + preamble->bootloader_address - preamble->body_load_address
53 * Useful metadata extends from b - 8KB through to b + 32 KB
57 PROBE_SIZE = SZ_4K, /* initial bytes read from partition */
59 X86_SETUP_OFFSET = -0x1000, /* setup offset relative to base */
60 CMDLINE_OFFSET = -0x2000, /* cmdline offset relative to base */
61 X86_KERNEL_OFFSET = 0x4000, /* kernel offset relative to base */
65 * struct cros_priv - Private data
67 * This is read from the disk and recorded for use when the full kernel must
68 * be loaded and booted
70 * @body_offset: Offset of kernel body from start of partition (in bytes)
71 * @body_size: Size of kernel body in bytes
72 * @part_start: Block offset of selected partition from the start of the disk
73 * @body_load_address: Nominal load address for kernel body
74 * @bootloader_address: Address of bootloader, after body is loaded at
76 * @bootloader_size: Size of bootloader in bytes
77 * @info_buf: Buffer containing ChromiumOS info
83 ulong body_load_address;
84 ulong bootloader_address;
85 ulong bootloader_size;
89 static int cros_check(struct udevice *dev, struct bootflow_iter *iter)
91 /* This only works on block and network devices */
92 if (bootflow_iter_check_blk(iter))
93 return log_msg_ret("blk", -ENOTSUPP);
98 static int copy_cmdline(const char *from, const char *uuid, char **bufp)
100 const int maxlen = 2048;
102 char *cmd, *to, *end;
105 /* Allow space for cmdline + UUID */
106 len = strnlen(from, sizeof(buf));
110 log_debug("uuid %d %s\n", uuid ? (int)strlen(uuid) : 0, uuid);
111 for (to = buf, end = buf + maxlen - UUID_STR_LEN - 1; *from; from++) {
114 if (from[0] == '%' && from[1] == 'U' && uuid &&
115 strlen(uuid) == UUID_STR_LEN) {
135 * scan_part() - Scan a kernel partition to see if has a ChromeOS header
137 * This reads the first PROBE_SIZE of a partition, loookng for
140 * @blk: Block device to scan
141 * @partnum: Partition number to scan
142 * @info: Please to put partition info
143 * @hdrp: Return allocated keyblock header on success
145 static int scan_part(struct udevice *blk, int partnum,
146 struct disk_partition *info, struct vb2_keyblock **hdrp)
148 struct blk_desc *desc = dev_get_uclass_plat(blk);
149 struct vb2_keyblock *hdr;
155 return log_msg_ret("efi", -ENOENT);
157 ret = part_get_info(desc, partnum, info);
159 return log_msg_ret("part", ret);
161 /* Check for kernel partition type */
162 log_debug("part %x: type=%s\n", partnum, info->type_guid);
163 if (uuid_str_to_bin(info->type_guid, type.b, UUID_STR_FORMAT_GUID))
164 return log_msg_ret("typ", -EINVAL);
166 if (guidcmp(&cros_kern_type, &type))
167 return log_msg_ret("typ", -ENOEXEC);
169 /* Make a buffer for the header information */
170 num_blks = PROBE_SIZE >> desc->log2blksz;
171 log_debug("Reading header, blk=%s, start=%lx, blocks=%lx\n",
172 blk->name, (ulong)info->start, num_blks);
173 hdr = memalign(SZ_1K, PROBE_SIZE);
175 return log_msg_ret("hdr", -ENOMEM);
176 ret = blk_read(blk, info->start, num_blks, hdr);
177 if (ret != num_blks) {
179 return log_msg_ret("inf", -EIO);
182 if (memcmp(VB2_KEYBLOCK_MAGIC, hdr->magic, VB2_KEYBLOCK_MAGIC_SIZE)) {
184 log_debug("no magic\n");
194 * cros_read_buf() - Read information into a buf and parse it
196 * @bflow: Bootflow to update
197 * @buf: Buffer to use
198 * @size: Size of buffer and number of bytes to read thereinto
199 * @start: Start offset to read from on disk
200 * @before_base: Number of bytes to read before the bootloader base
201 * @uuid: UUID string if supported, else NULL
202 * Return: 0 if OK, -ENOMEM if out of memory, -EIO on read failure
204 static int cros_read_buf(struct bootflow *bflow, void *buf, ulong size,
205 loff_t start, ulong before_base, const char *uuid)
207 struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
208 ulong base, setup, cmdline, kern_base;
212 num_blks = size >> desc->log2blksz;
213 log_debug("Reading info to %lx, blk=%s, size=%lx, blocks=%lx\n",
214 (ulong)map_to_sysmem(buf), bflow->blk->name, size, num_blks);
215 ret = blk_read(bflow->blk, start, num_blks, buf);
217 return log_msg_ret("inf", -EIO);
218 base = map_to_sysmem(buf) + before_base;
220 setup = base + X86_SETUP_OFFSET;
221 cmdline = base + CMDLINE_OFFSET;
222 kern_base = base + X86_KERNEL_OFFSET;
223 log_debug("base %lx setup %lx cmdline %lx kern_base %lx\n", base,
224 setup, cmdline, kern_base);
229 version = zimage_get_kernel_version(map_sysmem(setup, 0),
230 map_sysmem(kern_base, 0));
231 log_debug("version %s\n", version);
233 bflow->name = strdup(version);
236 bflow->name = strdup("ChromeOS");
238 return log_msg_ret("nam", -ENOMEM);
239 bflow->os_name = strdup("ChromeOS");
241 return log_msg_ret("os", -ENOMEM);
243 ret = copy_cmdline(map_sysmem(cmdline, 0), uuid, &bflow->cmdline);
245 return log_msg_ret("cmd", ret);
246 bflow->x86_setup = map_sysmem(setup, 0);
252 * cros_read_info() - Read information and fill out the bootflow
254 * @bflow: Bootflow to update
255 * @uuid: UUID string if supported, else NULL
256 * @preamble: Kernel preamble information
257 * Return: 0 if OK, -ENOMEM if out of memory, -EIO on read failure
259 static int cros_read_info(struct bootflow *bflow, const char *uuid,
260 const struct vb2_kernel_preamble *preamble)
262 struct cros_priv *priv = bflow->bootmeth_priv;
263 struct udevice *blk = bflow->blk;
264 struct blk_desc *desc = dev_get_uclass_plat(blk);
265 ulong offset, size, before_base;
269 log_debug("Kernel preamble at %lx, version major %x, minor %x\n",
270 (ulong)map_to_sysmem(preamble),
271 preamble->header_version_major,
272 preamble->header_version_minor);
274 log_debug(" - load_address %lx, bl_addr %lx, bl_size %lx\n",
275 (ulong)preamble->body_load_address,
276 (ulong)preamble->bootloader_address,
277 (ulong)preamble->bootloader_size);
279 priv->body_size = preamble->body_signature.data_size;
280 priv->body_load_address = preamble->body_load_address;
281 priv->bootloader_address = preamble->bootloader_address;
282 priv->bootloader_size = preamble->bootloader_size;
283 log_debug("Kernel body at %lx size %lx\n", priv->body_offset,
286 /* Work out how many bytes to read before the bootloader base */
287 before_base = -CMDLINE_OFFSET;
289 /* Read the cmdline through to the end of the bootloader */
290 size = priv->bootloader_size + before_base;
291 offset = priv->body_offset +
292 (priv->bootloader_address - priv->body_load_address) +
296 return log_msg_ret("buf", -ENOMEM);
298 ret = cros_read_buf(bflow, buf, size,
299 priv->part_start + (offset >> desc->log2blksz),
302 /* Clear this since the buffer is invalid */
303 bflow->x86_setup = NULL;
305 return log_msg_ret("pro", ret);
307 priv->info_buf = buf;
312 static int cros_read_kernel(struct bootflow *bflow)
314 struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
315 struct cros_priv *priv = bflow->bootmeth_priv;
321 bflow->size = priv->body_size;
323 buf = memalign(SZ_1K, priv->body_size);
325 return log_msg_ret("buf", -ENOMEM);
327 /* Check that the header is not smaller than permitted */
328 if (priv->body_offset < PROBE_SIZE)
329 return log_msg_ret("san", EFAULT);
331 /* Read kernel body */
332 num_blks = priv->body_size >> desc->log2blksz;
333 log_debug("Reading body to %lx, blk=%s, size=%lx, blocks=%lx\n",
334 (ulong)map_to_sysmem(buf), bflow->blk->name, priv->body_size,
336 ret = blk_read(bflow->blk,
337 priv->part_start + (priv->body_offset >> desc->log2blksz),
340 return log_msg_ret("inf", -EIO);
341 base = map_to_sysmem(buf) + priv->bootloader_address -
342 priv->body_load_address;
343 setup = base + X86_SETUP_OFFSET;
346 bflow->x86_setup = map_sysmem(setup, 0);
351 static int cros_read_bootflow(struct udevice *dev, struct bootflow *bflow)
353 const struct vb2_kernel_preamble *preamble;
354 struct disk_partition info;
355 struct vb2_keyblock *hdr;
356 const char *uuid = NULL;
357 struct cros_priv *priv;
360 log_debug("starting, part=%x\n", bflow->part);
362 /* Check for kernel partitions */
363 ret = scan_part(bflow->blk, bflow->part, &info, &hdr);
365 log_debug("- scan failed: err=%d\n", ret);
366 return log_msg_ret("scan", ret);
369 priv = malloc(sizeof(struct cros_priv));
372 return log_msg_ret("buf", -ENOMEM);
374 bflow->bootmeth_priv = priv;
376 log_debug("Selected partition %d, header at %lx\n", bflow->part,
377 (ulong)map_to_sysmem(hdr));
379 /* Grab a few things from the preamble */
380 preamble = (void *)hdr + hdr->keyblock_size;
381 priv->body_offset = hdr->keyblock_size + preamble->preamble_size;
382 priv->part_start = info.start;
384 /* Now read everything we can learn about kernel */
385 #if CONFIG_IS_ENABLED(PARTITION_UUIDS)
388 ret = cros_read_info(bflow, uuid, preamble);
392 free(priv->info_buf);
394 return log_msg_ret("inf", ret);
396 bflow->size = priv->body_size;
397 bflow->state = BOOTFLOWST_READY;
402 static int cros_read_file(struct udevice *dev, struct bootflow *bflow,
403 const char *file_path, ulong addr, ulong *sizep)
408 #if CONFIG_IS_ENABLED(BOOTSTD_FULL)
409 static int cros_read_all(struct udevice *dev, struct bootflow *bflow)
414 return log_msg_ret("ld", -EALREADY);
415 ret = cros_read_kernel(bflow);
417 return log_msg_ret("rd", ret);
421 #endif /* BOOTSTD_FULL */
423 static int cros_boot(struct udevice *dev, struct bootflow *bflow)
428 ret = cros_read_kernel(bflow);
430 return log_msg_ret("rd", ret);
433 if (IS_ENABLED(CONFIG_X86)) {
434 ret = zboot_run(map_to_sysmem(bflow->buf), bflow->size, 0, 0,
435 map_to_sysmem(bflow->x86_setup),
438 ret = bootm_boot_start(map_to_sysmem(bflow->buf),
442 return log_msg_ret("go", ret);
445 static int cros_bootmeth_bind(struct udevice *dev)
447 struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
449 plat->desc = "ChromiumOS boot";
450 plat->flags = BOOTMETHF_ANY_PART;
455 static struct bootmeth_ops cros_bootmeth_ops = {
457 .read_bootflow = cros_read_bootflow,
458 .read_file = cros_read_file,
460 #if CONFIG_IS_ENABLED(BOOTSTD_FULL)
461 .read_all = cros_read_all,
462 #endif /* BOOTSTD_FULL */
465 static const struct udevice_id cros_bootmeth_ids[] = {
466 { .compatible = "u-boot,cros" },
470 U_BOOT_DRIVER(bootmeth_cros) = {
471 .name = "bootmeth_cros",
472 .id = UCLASS_BOOTMETH,
473 .of_match = cros_bootmeth_ids,
474 .ops = &cros_bootmeth_ops,
475 .bind = cros_bootmeth_bind,