1 // SPDX-License-Identifier: GPL-2.0+
3 * Bootmethod for ChromiumOS
5 * Copyright 2023 Google LLC
6 * Written by Simon Glass <sjg@chromium.org>
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
);
247 if (!bootflow_img_add(bflow
, "setup",
248 (enum bootflow_img_t
)IH_TYPE_X86_SETUP
,
250 return log_msg_ret("cri", -ENOMEM
);
252 bflow
->x86_setup
= map_sysmem(setup
, 0);
254 if (!bootflow_img_add(bflow
, "cmdline", BFI_CMDLINE
, cmdline
, 0x1000))
255 return log_msg_ret("crc", -ENOMEM
);
261 * cros_read_info() - Read information and fill out the bootflow
263 * @bflow: Bootflow to update
264 * @uuid: UUID string if supported, else NULL
265 * @preamble: Kernel preamble information
266 * Return: 0 if OK, -ENOMEM if out of memory, -EIO on read failure
268 static int cros_read_info(struct bootflow
*bflow
, const char *uuid
,
269 const struct vb2_kernel_preamble
*preamble
)
271 struct cros_priv
*priv
= bflow
->bootmeth_priv
;
272 struct udevice
*blk
= bflow
->blk
;
273 struct blk_desc
*desc
= dev_get_uclass_plat(blk
);
274 ulong offset
, size
, before_base
;
278 log_debug("Kernel preamble at %lx, version major %x, minor %x\n",
279 (ulong
)map_to_sysmem(preamble
),
280 preamble
->header_version_major
,
281 preamble
->header_version_minor
);
283 log_debug(" - load_address %lx, bl_addr %lx, bl_size %lx\n",
284 (ulong
)preamble
->body_load_address
,
285 (ulong
)preamble
->bootloader_address
,
286 (ulong
)preamble
->bootloader_size
);
288 priv
->body_size
= preamble
->body_signature
.data_size
;
289 priv
->body_load_address
= preamble
->body_load_address
;
290 priv
->bootloader_address
= preamble
->bootloader_address
;
291 priv
->bootloader_size
= preamble
->bootloader_size
;
292 log_debug("Kernel body at %lx size %lx\n", priv
->body_offset
,
295 /* Work out how many bytes to read before the bootloader base */
296 before_base
= -CMDLINE_OFFSET
;
298 /* Read the cmdline through to the end of the bootloader */
299 size
= priv
->bootloader_size
+ before_base
;
300 offset
= priv
->body_offset
+
301 (priv
->bootloader_address
- priv
->body_load_address
) +
305 return log_msg_ret("buf", -ENOMEM
);
307 ret
= cros_read_buf(bflow
, buf
, size
,
308 priv
->part_start
+ (offset
>> desc
->log2blksz
),
311 /* Clear this since the buffer is invalid */
312 bflow
->x86_setup
= NULL
;
314 return log_msg_ret("pro", ret
);
316 priv
->info_buf
= buf
;
318 if (!bootflow_img_add(bflow
, "kernel",
319 (enum bootflow_img_t
)IH_TYPE_KERNEL
, 0,
321 return log_msg_ret("crk", -ENOMEM
);
326 static int cros_read_kernel(struct bootflow
*bflow
)
328 struct blk_desc
*desc
= dev_get_uclass_plat(bflow
->blk
);
329 struct cros_priv
*priv
= bflow
->bootmeth_priv
;
335 bflow
->size
= priv
->body_size
;
337 buf
= memalign(SZ_1K
, priv
->body_size
);
339 return log_msg_ret("buf", -ENOMEM
);
341 /* Check that the header is not smaller than permitted */
342 if (priv
->body_offset
< PROBE_SIZE
)
343 return log_msg_ret("san", EFAULT
);
345 /* Read kernel body */
346 num_blks
= priv
->body_size
>> desc
->log2blksz
;
347 log_debug("Reading body to %lx, blk=%s, size=%lx, blocks=%lx\n",
348 (ulong
)map_to_sysmem(buf
), bflow
->blk
->name
, priv
->body_size
,
350 ret
= blk_read(bflow
->blk
,
351 priv
->part_start
+ (priv
->body_offset
>> desc
->log2blksz
),
354 return log_msg_ret("inf", -EIO
);
355 base
= map_to_sysmem(buf
) + priv
->bootloader_address
-
356 priv
->body_load_address
;
357 setup
= base
+ X86_SETUP_OFFSET
;
360 bflow
->x86_setup
= map_sysmem(setup
, 0);
365 static int cros_read_bootflow(struct udevice
*dev
, struct bootflow
*bflow
)
367 const struct vb2_kernel_preamble
*preamble
;
368 struct disk_partition info
;
369 struct vb2_keyblock
*hdr
;
370 const char *uuid
= NULL
;
371 struct cros_priv
*priv
;
374 log_debug("starting, part=%x\n", bflow
->part
);
376 /* Check for kernel partitions */
377 ret
= scan_part(bflow
->blk
, bflow
->part
, &info
, &hdr
);
379 log_debug("- scan failed: err=%d\n", ret
);
380 return log_msg_ret("scan", ret
);
383 priv
= malloc(sizeof(struct cros_priv
));
386 return log_msg_ret("buf", -ENOMEM
);
388 bflow
->bootmeth_priv
= priv
;
390 log_debug("Selected partition %d, header at %lx\n", bflow
->part
,
391 (ulong
)map_to_sysmem(hdr
));
393 /* Grab a few things from the preamble */
394 preamble
= (void *)hdr
+ hdr
->keyblock_size
;
395 priv
->body_offset
= hdr
->keyblock_size
+ preamble
->preamble_size
;
396 priv
->part_start
= info
.start
;
398 /* Now read everything we can learn about kernel */
399 #if CONFIG_IS_ENABLED(PARTITION_UUIDS)
402 ret
= cros_read_info(bflow
, uuid
, preamble
);
406 free(priv
->info_buf
);
408 return log_msg_ret("inf", ret
);
410 bflow
->size
= priv
->body_size
;
411 bflow
->state
= BOOTFLOWST_READY
;
416 static int cros_read_file(struct udevice
*dev
, struct bootflow
*bflow
,
417 const char *file_path
, ulong addr
,
418 enum bootflow_img_t type
, ulong
*sizep
)
423 #if CONFIG_IS_ENABLED(BOOTSTD_FULL)
424 static int cros_read_all(struct udevice
*dev
, struct bootflow
*bflow
)
429 return log_msg_ret("ld", -EALREADY
);
430 ret
= cros_read_kernel(bflow
);
432 return log_msg_ret("rd", ret
);
436 #endif /* BOOTSTD_FULL */
438 static int cros_boot(struct udevice
*dev
, struct bootflow
*bflow
)
443 ret
= cros_read_kernel(bflow
);
445 return log_msg_ret("rd", ret
);
448 if (IS_ENABLED(CONFIG_X86
)) {
449 ret
= zboot_run(map_to_sysmem(bflow
->buf
), bflow
->size
, 0, 0,
450 map_to_sysmem(bflow
->x86_setup
),
453 ret
= bootm_boot_start(map_to_sysmem(bflow
->buf
),
457 return log_msg_ret("go", ret
);
460 static int cros_bootmeth_bind(struct udevice
*dev
)
462 struct bootmeth_uc_plat
*plat
= dev_get_uclass_plat(dev
);
464 plat
->desc
= "ChromiumOS boot";
465 plat
->flags
= BOOTMETHF_ANY_PART
;
470 static struct bootmeth_ops cros_bootmeth_ops
= {
472 .read_bootflow
= cros_read_bootflow
,
473 .read_file
= cros_read_file
,
475 #if CONFIG_IS_ENABLED(BOOTSTD_FULL)
476 .read_all
= cros_read_all
,
477 #endif /* BOOTSTD_FULL */
480 static const struct udevice_id cros_bootmeth_ids
[] = {
481 { .compatible
= "u-boot,cros" },
485 U_BOOT_DRIVER(bootmeth_cros
) = {
486 .name
= "bootmeth_cros",
487 .id
= UCLASS_BOOTMETH
,
488 .of_match
= cros_bootmeth_ids
,
489 .ops
= &cros_bootmeth_ops
,
490 .bind
= cros_bootmeth_bind
,