blob: c7b862e512a00d57c3aed0e96bd8ce9df044bb7b [file] [log] [blame]
Simon Glassd74e62a2023-07-12 09:04:45 -06001// 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
9#define LOG_CATEGORY UCLASS_BOOTSTD
10
Simon Glassd74e62a2023-07-12 09:04:45 -060011#include <blk.h>
12#include <bootdev.h>
13#include <bootflow.h>
Simon Glass4493d612023-07-30 11:16:53 -060014#include <bootm.h>
Simon Glassd74e62a2023-07-12 09:04:45 -060015#include <bootmeth.h>
Simon Glass3e62d802023-07-30 11:16:50 -060016#include <display_options.h>
Simon Glassd74e62a2023-07-12 09:04:45 -060017#include <dm.h>
Simon Glassd7d3a972023-08-24 13:55:45 -060018#include <efi.h>
Simon Glassd74e62a2023-07-12 09:04:45 -060019#include <malloc.h>
20#include <mapmem.h>
21#include <part.h>
Simon Glassd74e62a2023-07-12 09:04:45 -060022#include <linux/sizes.h>
Simon Glass7c6ab822023-07-30 11:16:49 -060023#include "bootmeth_cros.h"
Simon Glassd74e62a2023-07-12 09:04:45 -060024
Simon Glassd7d3a972023-08-24 13:55:45 -060025static const efi_guid_t cros_kern_type = PARTITION_CROS_KERNEL;
26
Simon Glassd5308922023-07-30 11:16:54 -060027/*
28 * Layout of the ChromeOS kernel
29 *
Simon Glassd7d3a972023-08-24 13:55:45 -060030 * Partitions 2 and 4 contain kernels with type GUID_CROS_KERNEL
Simon Glassd5308922023-07-30 11:16:54 -060031 *
32 * Contents are:
33 *
34 * Offset Contents
35 * 0 struct vb2_keyblock
36 * m struct vb2_kernel_preamble
37 * m + n kernel buffer
38 *
39 * m is keyblock->keyblock_size
40 * n is preamble->preamble_size
41 *
42 * The kernel buffer itself consists of various parts:
43 *
44 * Offset Contents
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)
50 *
51 * b is m + n + preamble->bootloader_address - preamble->body_load_address
52 *
53 * Useful metadata extends from b - 8KB through to b + 32 KB
54 */
55
Simon Glassd74e62a2023-07-12 09:04:45 -060056enum {
Simon Glass09ebe822023-07-30 11:16:48 -060057 PROBE_SIZE = SZ_4K, /* initial bytes read from partition */
58
Simon Glass016dc602023-07-30 11:16:52 -060059 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 */
Simon Glassd74e62a2023-07-12 09:04:45 -060062};
63
Simon Glass71138d32023-07-30 11:16:55 -060064/**
65 * struct cros_priv - Private data
66 *
67 * This is read from the disk and recorded for use when the full kernel must
68 * be loaded and booted
69 *
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
75 * body_load_address
76 * @bootloader_size: Size of bootloader in bytes
Simon Glass37b61c12023-07-30 11:16:57 -060077 * @info_buf: Buffer containing ChromiumOS info
Simon Glass71138d32023-07-30 11:16:55 -060078 */
79struct cros_priv {
80 ulong body_offset;
81 ulong body_size;
82 lbaint_t part_start;
83 ulong body_load_address;
84 ulong bootloader_address;
85 ulong bootloader_size;
Simon Glass37b61c12023-07-30 11:16:57 -060086 void *info_buf;
Simon Glass71138d32023-07-30 11:16:55 -060087};
88
Simon Glassd74e62a2023-07-12 09:04:45 -060089static int cros_check(struct udevice *dev, struct bootflow_iter *iter)
90{
91 /* This only works on block and network devices */
92 if (bootflow_iter_check_blk(iter))
93 return log_msg_ret("blk", -ENOTSUPP);
94
95 return 0;
96}
97
98static int copy_cmdline(const char *from, const char *uuid, char **bufp)
99{
100 const int maxlen = 2048;
101 char buf[maxlen];
102 char *cmd, *to, *end;
103 int len;
104
105 /* Allow space for cmdline + UUID */
106 len = strnlen(from, sizeof(buf));
107 if (len >= maxlen)
108 return -E2BIG;
109
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++) {
112 if (to >= end)
113 return -E2BIG;
114 if (from[0] == '%' && from[1] == 'U' && uuid &&
115 strlen(uuid) == UUID_STR_LEN) {
116 strcpy(to, uuid);
117 to += UUID_STR_LEN;
118 from++;
119 } else {
120 *to++ = *from;
121 }
122 }
123 *to = '\0';
124 len = to - buf;
125 cmd = strdup(buf);
126 if (!cmd)
127 return -ENOMEM;
128 free(*bufp);
129 *bufp = cmd;
130
131 return 0;
132}
133
Simon Glass09ebe822023-07-30 11:16:48 -0600134/**
135 * scan_part() - Scan a kernel partition to see if has a ChromeOS header
136 *
Simon Glass7c6ab822023-07-30 11:16:49 -0600137 * This reads the first PROBE_SIZE of a partition, loookng for
138 * VB2_KEYBLOCK_MAGIC
Simon Glass09ebe822023-07-30 11:16:48 -0600139 *
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
144 */
145static int scan_part(struct udevice *blk, int partnum,
Simon Glass05cfcd02023-07-30 11:16:51 -0600146 struct disk_partition *info, struct vb2_keyblock **hdrp)
Simon Glass09ebe822023-07-30 11:16:48 -0600147{
148 struct blk_desc *desc = dev_get_uclass_plat(blk);
149 struct vb2_keyblock *hdr;
Vincent Stehlé9befbc82024-07-03 13:37:56 +0200150 efi_guid_t type;
Simon Glass09ebe822023-07-30 11:16:48 -0600151 ulong num_blks;
152 int ret;
153
Simon Glassd7d3a972023-08-24 13:55:45 -0600154 if (!partnum)
155 return log_msg_ret("efi", -ENOENT);
156
Simon Glass09ebe822023-07-30 11:16:48 -0600157 ret = part_get_info(desc, partnum, info);
158 if (ret)
159 return log_msg_ret("part", ret);
160
Simon Glassd7d3a972023-08-24 13:55:45 -0600161 /* Check for kernel partition type */
162 log_debug("part %x: type=%s\n", partnum, info->type_guid);
Vincent Stehlé9befbc82024-07-03 13:37:56 +0200163 if (uuid_str_to_bin(info->type_guid, type.b, UUID_STR_FORMAT_GUID))
Simon Glassd7d3a972023-08-24 13:55:45 -0600164 return log_msg_ret("typ", -EINVAL);
165
Vincent Stehlé9befbc82024-07-03 13:37:56 +0200166 if (guidcmp(&cros_kern_type, &type))
Simon Glassd7d3a972023-08-24 13:55:45 -0600167 return log_msg_ret("typ", -ENOEXEC);
168
Simon Glass09ebe822023-07-30 11:16:48 -0600169 /* 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);
174 if (!hdr)
175 return log_msg_ret("hdr", -ENOMEM);
176 ret = blk_read(blk, info->start, num_blks, hdr);
177 if (ret != num_blks) {
178 free(hdr);
179 return log_msg_ret("inf", -EIO);
180 }
181
Simon Glass7c6ab822023-07-30 11:16:49 -0600182 if (memcmp(VB2_KEYBLOCK_MAGIC, hdr->magic, VB2_KEYBLOCK_MAGIC_SIZE)) {
Simon Glass09ebe822023-07-30 11:16:48 -0600183 free(hdr);
Simon Glassd7d3a972023-08-24 13:55:45 -0600184 log_debug("no magic\n");
Simon Glass09ebe822023-07-30 11:16:48 -0600185 return -ENOENT;
186 }
187
188 *hdrp = hdr;
189
190 return 0;
191}
192
Simon Glass37b61c12023-07-30 11:16:57 -0600193/**
194 * cros_read_buf() - Read information into a buf and parse it
195 *
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
203 */
204static int cros_read_buf(struct bootflow *bflow, void *buf, ulong size,
205 loff_t start, ulong before_base, const char *uuid)
206{
207 struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
208 ulong base, setup, cmdline, kern_base;
209 ulong num_blks;
210 int ret;
211
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);
216 if (ret != num_blks)
217 return log_msg_ret("inf", -EIO);
218 base = map_to_sysmem(buf) + before_base;
219
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);
225
226#ifdef CONFIG_X86
227 const char *version;
228
229 version = zimage_get_kernel_version(map_sysmem(setup, 0),
230 map_sysmem(kern_base, 0));
231 log_debug("version %s\n", version);
232 if (version)
233 bflow->name = strdup(version);
234#endif
235 if (!bflow->name)
236 bflow->name = strdup("ChromeOS");
237 if (!bflow->name)
238 return log_msg_ret("nam", -ENOMEM);
239 bflow->os_name = strdup("ChromeOS");
240 if (!bflow->os_name)
241 return log_msg_ret("os", -ENOMEM);
242
243 ret = copy_cmdline(map_sysmem(cmdline, 0), uuid, &bflow->cmdline);
244 if (ret)
245 return log_msg_ret("cmd", ret);
Simon Glass8c870122024-11-15 16:19:22 -0700246
247 if (!bootflow_img_add(bflow, "setup",
248 (enum bootflow_img_t)IH_TYPE_X86_SETUP,
249 setup, 0x3000))
250 return log_msg_ret("cri", -ENOMEM);
251
Simon Glass37b61c12023-07-30 11:16:57 -0600252 bflow->x86_setup = map_sysmem(setup, 0);
253
Simon Glass8c870122024-11-15 16:19:22 -0700254 if (!bootflow_img_add(bflow, "cmdline", BFI_CMDLINE, cmdline, 0x1000))
255 return log_msg_ret("crc", -ENOMEM);
256
Simon Glass37b61c12023-07-30 11:16:57 -0600257 return 0;
258}
259
260/**
261 * cros_read_info() - Read information and fill out the bootflow
262 *
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
267 */
268static int cros_read_info(struct bootflow *bflow, const char *uuid,
269 const struct vb2_kernel_preamble *preamble)
270{
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;
275 void *buf;
276 int ret;
277
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);
282
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);
287
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,
293 priv->body_size);
294
295 /* Work out how many bytes to read before the bootloader base */
296 before_base = -CMDLINE_OFFSET;
297
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) +
302 CMDLINE_OFFSET;
303 buf = malloc(size);
304 if (!buf)
305 return log_msg_ret("buf", -ENOMEM);
306
307 ret = cros_read_buf(bflow, buf, size,
308 priv->part_start + (offset >> desc->log2blksz),
309 before_base, uuid);
310 if (ret) {
311 /* Clear this since the buffer is invalid */
312 bflow->x86_setup = NULL;
313 free(buf);
314 return log_msg_ret("pro", ret);
315 }
316 priv->info_buf = buf;
317
Simon Glass8c870122024-11-15 16:19:22 -0700318 if (!bootflow_img_add(bflow, "kernel",
319 (enum bootflow_img_t)IH_TYPE_KERNEL, 0,
320 priv->body_size))
321 return log_msg_ret("crk", -ENOMEM);
322
Simon Glass37b61c12023-07-30 11:16:57 -0600323 return 0;
324}
325
Simon Glass26279262023-07-30 11:16:58 -0600326static int cros_read_kernel(struct bootflow *bflow)
327{
328 struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
329 struct cros_priv *priv = bflow->bootmeth_priv;
330 ulong base, setup;
331 ulong num_blks;
332 void *buf;
333 int ret;
334
335 bflow->size = priv->body_size;
336
337 buf = memalign(SZ_1K, priv->body_size);
338 if (!buf)
339 return log_msg_ret("buf", -ENOMEM);
340
341 /* Check that the header is not smaller than permitted */
342 if (priv->body_offset < PROBE_SIZE)
343 return log_msg_ret("san", EFAULT);
344
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,
349 num_blks);
350 ret = blk_read(bflow->blk,
351 priv->part_start + (priv->body_offset >> desc->log2blksz),
352 num_blks, buf);
353 if (ret != num_blks)
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;
358
359 bflow->buf = buf;
360 bflow->x86_setup = map_sysmem(setup, 0);
361
362 return 0;
363}
364
Simon Glassd74e62a2023-07-12 09:04:45 -0600365static int cros_read_bootflow(struct udevice *dev, struct bootflow *bflow)
366{
Simon Glass05cfcd02023-07-30 11:16:51 -0600367 const struct vb2_kernel_preamble *preamble;
Simon Glassd74e62a2023-07-12 09:04:45 -0600368 struct disk_partition info;
Simon Glass05cfcd02023-07-30 11:16:51 -0600369 struct vb2_keyblock *hdr;
Simon Glass4fde2062023-07-30 11:16:59 -0600370 const char *uuid = NULL;
371 struct cros_priv *priv;
Simon Glassd7d3a972023-08-24 13:55:45 -0600372 int ret;
Simon Glassd74e62a2023-07-12 09:04:45 -0600373
Simon Glassd7d3a972023-08-24 13:55:45 -0600374 log_debug("starting, part=%x\n", bflow->part);
Simon Glassd74e62a2023-07-12 09:04:45 -0600375
Simon Glassd7d3a972023-08-24 13:55:45 -0600376 /* Check for kernel partitions */
377 ret = scan_part(bflow->blk, bflow->part, &info, &hdr);
Simon Glass3e62d802023-07-30 11:16:50 -0600378 if (ret) {
Simon Glassd7d3a972023-08-24 13:55:45 -0600379 log_debug("- scan failed: err=%d\n", ret);
380 return log_msg_ret("scan", ret);
Simon Glass3e62d802023-07-30 11:16:50 -0600381 }
Simon Glassd74e62a2023-07-12 09:04:45 -0600382
Simon Glass4fde2062023-07-30 11:16:59 -0600383 priv = malloc(sizeof(struct cros_priv));
384 if (!priv) {
385 free(hdr);
386 return log_msg_ret("buf", -ENOMEM);
387 }
388 bflow->bootmeth_priv = priv;
Simon Glass05cfcd02023-07-30 11:16:51 -0600389
Simon Glassd7d3a972023-08-24 13:55:45 -0600390 log_debug("Selected partition %d, header at %lx\n", bflow->part,
391 (ulong)map_to_sysmem(hdr));
Simon Glassd74e62a2023-07-12 09:04:45 -0600392
Simon Glass4fde2062023-07-30 11:16:59 -0600393 /* Grab a few things from the preamble */
394 preamble = (void *)hdr + hdr->keyblock_size;
Simon Glass71138d32023-07-30 11:16:55 -0600395 priv->body_offset = hdr->keyblock_size + preamble->preamble_size;
396 priv->part_start = info.start;
Simon Glass05cfcd02023-07-30 11:16:51 -0600397
Simon Glass4fde2062023-07-30 11:16:59 -0600398 /* Now read everything we can learn about kernel */
Simon Glassd74e62a2023-07-12 09:04:45 -0600399#if CONFIG_IS_ENABLED(PARTITION_UUIDS)
400 uuid = info.uuid;
401#endif
Simon Glass4fde2062023-07-30 11:16:59 -0600402 ret = cros_read_info(bflow, uuid, preamble);
403 preamble = NULL;
404 free(hdr);
Simon Glassd7d3a972023-08-24 13:55:45 -0600405 if (ret) {
406 free(priv->info_buf);
407 free(priv);
Simon Glass4fde2062023-07-30 11:16:59 -0600408 return log_msg_ret("inf", ret);
Simon Glassd7d3a972023-08-24 13:55:45 -0600409 }
Simon Glass4fde2062023-07-30 11:16:59 -0600410 bflow->size = priv->body_size;
Simon Glassd74e62a2023-07-12 09:04:45 -0600411 bflow->state = BOOTFLOWST_READY;
Simon Glassd74e62a2023-07-12 09:04:45 -0600412
413 return 0;
414}
415
416static int cros_read_file(struct udevice *dev, struct bootflow *bflow,
Simon Glassf39b5592024-11-15 16:19:17 -0700417 const char *file_path, ulong addr,
418 enum bootflow_img_t type, ulong *sizep)
Simon Glassd74e62a2023-07-12 09:04:45 -0600419{
420 return -ENOSYS;
421}
422
Simon Glass55f58322023-10-25 07:17:37 +1300423#if CONFIG_IS_ENABLED(BOOTSTD_FULL)
Simon Glass6d8f95b2023-08-10 19:33:18 -0600424static int cros_read_all(struct udevice *dev, struct bootflow *bflow)
Simon Glassd74e62a2023-07-12 09:04:45 -0600425{
Simon Glass4fde2062023-07-30 11:16:59 -0600426 int ret;
427
Simon Glass6d8f95b2023-08-10 19:33:18 -0600428 if (bflow->buf)
429 return log_msg_ret("ld", -EALREADY);
Simon Glass4fde2062023-07-30 11:16:59 -0600430 ret = cros_read_kernel(bflow);
431 if (ret)
432 return log_msg_ret("rd", ret);
Simon Glass6d8f95b2023-08-10 19:33:18 -0600433
434 return 0;
435}
Simon Glass55f58322023-10-25 07:17:37 +1300436#endif /* BOOTSTD_FULL */
Simon Glass6d8f95b2023-08-10 19:33:18 -0600437
438static int cros_boot(struct udevice *dev, struct bootflow *bflow)
439{
440 int ret;
441
442 if (!bflow->buf) {
443 ret = cros_read_kernel(bflow);
444 if (ret)
445 return log_msg_ret("rd", ret);
446 }
Simon Glass18700262023-07-30 11:17:02 -0600447
448 if (IS_ENABLED(CONFIG_X86)) {
Simon Glass365d17b2023-12-03 17:29:35 -0700449 ret = zboot_run(map_to_sysmem(bflow->buf), bflow->size, 0, 0,
450 map_to_sysmem(bflow->x86_setup),
451 bflow->cmdline);
Simon Glass18700262023-07-30 11:17:02 -0600452 } else {
453 ret = bootm_boot_start(map_to_sysmem(bflow->buf),
454 bflow->cmdline);
455 }
Simon Glassd74e62a2023-07-12 09:04:45 -0600456
Simon Glass18700262023-07-30 11:17:02 -0600457 return log_msg_ret("go", ret);
Simon Glassd74e62a2023-07-12 09:04:45 -0600458}
459
460static int cros_bootmeth_bind(struct udevice *dev)
461{
462 struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
463
464 plat->desc = "ChromiumOS boot";
Simon Glassd7d3a972023-08-24 13:55:45 -0600465 plat->flags = BOOTMETHF_ANY_PART;
Simon Glassd74e62a2023-07-12 09:04:45 -0600466
467 return 0;
468}
469
470static struct bootmeth_ops cros_bootmeth_ops = {
471 .check = cros_check,
472 .read_bootflow = cros_read_bootflow,
473 .read_file = cros_read_file,
474 .boot = cros_boot,
Simon Glass55f58322023-10-25 07:17:37 +1300475#if CONFIG_IS_ENABLED(BOOTSTD_FULL)
Simon Glass6d8f95b2023-08-10 19:33:18 -0600476 .read_all = cros_read_all,
Simon Glass55f58322023-10-25 07:17:37 +1300477#endif /* BOOTSTD_FULL */
Simon Glassd74e62a2023-07-12 09:04:45 -0600478};
479
480static const struct udevice_id cros_bootmeth_ids[] = {
481 { .compatible = "u-boot,cros" },
482 { }
483};
484
485U_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,
491};