blob: 20e0b1e89c360168fa14eb12ba6b464fbbf61e0d [file] [log] [blame]
Simon Glassc88d67d2023-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
11#include <common.h>
12#include <blk.h>
13#include <bootdev.h>
14#include <bootflow.h>
Simon Glass1a081092023-07-30 11:16:53 -060015#include <bootm.h>
Simon Glassc88d67d2023-07-12 09:04:45 -060016#include <bootmeth.h>
Simon Glass4cfe4512023-07-30 11:16:50 -060017#include <display_options.h>
Simon Glassc88d67d2023-07-12 09:04:45 -060018#include <dm.h>
Simon Glass71f634b2023-08-24 13:55:45 -060019#include <efi.h>
Simon Glassc88d67d2023-07-12 09:04:45 -060020#include <malloc.h>
21#include <mapmem.h>
22#include <part.h>
Simon Glassc88d67d2023-07-12 09:04:45 -060023#include <linux/sizes.h>
Simon Glassde30aa92023-07-30 11:16:49 -060024#include "bootmeth_cros.h"
Simon Glassc88d67d2023-07-12 09:04:45 -060025
Simon Glass71f634b2023-08-24 13:55:45 -060026static const efi_guid_t cros_kern_type = PARTITION_CROS_KERNEL;
27
Simon Glassb7ed5382023-07-30 11:16:54 -060028/*
29 * Layout of the ChromeOS kernel
30 *
Simon Glass71f634b2023-08-24 13:55:45 -060031 * Partitions 2 and 4 contain kernels with type GUID_CROS_KERNEL
Simon Glassb7ed5382023-07-30 11:16:54 -060032 *
33 * Contents are:
34 *
35 * Offset Contents
36 * 0 struct vb2_keyblock
37 * m struct vb2_kernel_preamble
38 * m + n kernel buffer
39 *
40 * m is keyblock->keyblock_size
41 * n is preamble->preamble_size
42 *
43 * The kernel buffer itself consists of various parts:
44 *
45 * Offset Contents
46 * m + n kernel image (Flat vmlinux binary or FIT)
47 * b - 8KB Command line text
48 * b - 4KB X86 setup block (struct boot_params, extends for about 16KB)
49 * b X86 bootloader (continuation of setup block)
50 * b + 16KB X86 setup block (copy, used for hold data pointed to)
51 *
52 * b is m + n + preamble->bootloader_address - preamble->body_load_address
53 *
54 * Useful metadata extends from b - 8KB through to b + 32 KB
55 */
56
Simon Glassc88d67d2023-07-12 09:04:45 -060057enum {
Simon Glass5a8589e2023-07-30 11:16:48 -060058 PROBE_SIZE = SZ_4K, /* initial bytes read from partition */
59
Simon Glassc5dca502023-07-30 11:16:52 -060060 X86_SETUP_OFFSET = -0x1000, /* setup offset relative to base */
61 CMDLINE_OFFSET = -0x2000, /* cmdline offset relative to base */
62 X86_KERNEL_OFFSET = 0x4000, /* kernel offset relative to base */
Simon Glassc88d67d2023-07-12 09:04:45 -060063};
64
Simon Glass1d4bbdf2023-07-30 11:16:55 -060065/**
66 * struct cros_priv - Private data
67 *
68 * This is read from the disk and recorded for use when the full kernel must
69 * be loaded and booted
70 *
71 * @body_offset: Offset of kernel body from start of partition (in bytes)
72 * @body_size: Size of kernel body in bytes
73 * @part_start: Block offset of selected partition from the start of the disk
74 * @body_load_address: Nominal load address for kernel body
75 * @bootloader_address: Address of bootloader, after body is loaded at
76 * body_load_address
77 * @bootloader_size: Size of bootloader in bytes
Simon Glassf861b1e2023-07-30 11:16:57 -060078 * @info_buf: Buffer containing ChromiumOS info
Simon Glass1d4bbdf2023-07-30 11:16:55 -060079 */
80struct cros_priv {
81 ulong body_offset;
82 ulong body_size;
83 lbaint_t part_start;
84 ulong body_load_address;
85 ulong bootloader_address;
86 ulong bootloader_size;
Simon Glassf861b1e2023-07-30 11:16:57 -060087 void *info_buf;
Simon Glass1d4bbdf2023-07-30 11:16:55 -060088};
89
Simon Glassc88d67d2023-07-12 09:04:45 -060090static int cros_check(struct udevice *dev, struct bootflow_iter *iter)
91{
92 /* This only works on block and network devices */
93 if (bootflow_iter_check_blk(iter))
94 return log_msg_ret("blk", -ENOTSUPP);
95
96 return 0;
97}
98
99static int copy_cmdline(const char *from, const char *uuid, char **bufp)
100{
101 const int maxlen = 2048;
102 char buf[maxlen];
103 char *cmd, *to, *end;
104 int len;
105
106 /* Allow space for cmdline + UUID */
107 len = strnlen(from, sizeof(buf));
108 if (len >= maxlen)
109 return -E2BIG;
110
111 log_debug("uuid %d %s\n", uuid ? (int)strlen(uuid) : 0, uuid);
112 for (to = buf, end = buf + maxlen - UUID_STR_LEN - 1; *from; from++) {
113 if (to >= end)
114 return -E2BIG;
115 if (from[0] == '%' && from[1] == 'U' && uuid &&
116 strlen(uuid) == UUID_STR_LEN) {
117 strcpy(to, uuid);
118 to += UUID_STR_LEN;
119 from++;
120 } else {
121 *to++ = *from;
122 }
123 }
124 *to = '\0';
125 len = to - buf;
126 cmd = strdup(buf);
127 if (!cmd)
128 return -ENOMEM;
129 free(*bufp);
130 *bufp = cmd;
131
132 return 0;
133}
134
Simon Glass5a8589e2023-07-30 11:16:48 -0600135/**
136 * scan_part() - Scan a kernel partition to see if has a ChromeOS header
137 *
Simon Glassde30aa92023-07-30 11:16:49 -0600138 * This reads the first PROBE_SIZE of a partition, loookng for
139 * VB2_KEYBLOCK_MAGIC
Simon Glass5a8589e2023-07-30 11:16:48 -0600140 *
141 * @blk: Block device to scan
142 * @partnum: Partition number to scan
143 * @info: Please to put partition info
144 * @hdrp: Return allocated keyblock header on success
145 */
146static int scan_part(struct udevice *blk, int partnum,
Simon Glass32578352023-07-30 11:16:51 -0600147 struct disk_partition *info, struct vb2_keyblock **hdrp)
Simon Glass5a8589e2023-07-30 11:16:48 -0600148{
149 struct blk_desc *desc = dev_get_uclass_plat(blk);
150 struct vb2_keyblock *hdr;
Simon Glass71f634b2023-08-24 13:55:45 -0600151 struct uuid type;
Simon Glass5a8589e2023-07-30 11:16:48 -0600152 ulong num_blks;
153 int ret;
154
Simon Glass71f634b2023-08-24 13:55:45 -0600155 if (!partnum)
156 return log_msg_ret("efi", -ENOENT);
157
Simon Glass5a8589e2023-07-30 11:16:48 -0600158 ret = part_get_info(desc, partnum, info);
159 if (ret)
160 return log_msg_ret("part", ret);
161
Simon Glass71f634b2023-08-24 13:55:45 -0600162 /* Check for kernel partition type */
163 log_debug("part %x: type=%s\n", partnum, info->type_guid);
164 if (uuid_str_to_bin(info->type_guid, (u8 *)&type, UUID_STR_FORMAT_GUID))
165 return log_msg_ret("typ", -EINVAL);
166
167 if (memcmp(&cros_kern_type, &type, sizeof(type)))
168 return log_msg_ret("typ", -ENOEXEC);
169
Simon Glass5a8589e2023-07-30 11:16:48 -0600170 /* Make a buffer for the header information */
171 num_blks = PROBE_SIZE >> desc->log2blksz;
172 log_debug("Reading header, blk=%s, start=%lx, blocks=%lx\n",
173 blk->name, (ulong)info->start, num_blks);
174 hdr = memalign(SZ_1K, PROBE_SIZE);
175 if (!hdr)
176 return log_msg_ret("hdr", -ENOMEM);
177 ret = blk_read(blk, info->start, num_blks, hdr);
178 if (ret != num_blks) {
179 free(hdr);
180 return log_msg_ret("inf", -EIO);
181 }
182
Simon Glassde30aa92023-07-30 11:16:49 -0600183 if (memcmp(VB2_KEYBLOCK_MAGIC, hdr->magic, VB2_KEYBLOCK_MAGIC_SIZE)) {
Simon Glass5a8589e2023-07-30 11:16:48 -0600184 free(hdr);
Simon Glass71f634b2023-08-24 13:55:45 -0600185 log_debug("no magic\n");
Simon Glass5a8589e2023-07-30 11:16:48 -0600186 return -ENOENT;
187 }
188
189 *hdrp = hdr;
190
191 return 0;
192}
193
Simon Glassf861b1e2023-07-30 11:16:57 -0600194/**
195 * cros_read_buf() - Read information into a buf and parse it
196 *
197 * @bflow: Bootflow to update
198 * @buf: Buffer to use
199 * @size: Size of buffer and number of bytes to read thereinto
200 * @start: Start offset to read from on disk
201 * @before_base: Number of bytes to read before the bootloader base
202 * @uuid: UUID string if supported, else NULL
203 * Return: 0 if OK, -ENOMEM if out of memory, -EIO on read failure
204 */
205static int cros_read_buf(struct bootflow *bflow, void *buf, ulong size,
206 loff_t start, ulong before_base, const char *uuid)
207{
208 struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
209 ulong base, setup, cmdline, kern_base;
210 ulong num_blks;
211 int ret;
212
213 num_blks = size >> desc->log2blksz;
214 log_debug("Reading info to %lx, blk=%s, size=%lx, blocks=%lx\n",
215 (ulong)map_to_sysmem(buf), bflow->blk->name, size, num_blks);
216 ret = blk_read(bflow->blk, start, num_blks, buf);
217 if (ret != num_blks)
218 return log_msg_ret("inf", -EIO);
219 base = map_to_sysmem(buf) + before_base;
220
221 setup = base + X86_SETUP_OFFSET;
222 cmdline = base + CMDLINE_OFFSET;
223 kern_base = base + X86_KERNEL_OFFSET;
224 log_debug("base %lx setup %lx cmdline %lx kern_base %lx\n", base,
225 setup, cmdline, kern_base);
226
227#ifdef CONFIG_X86
228 const char *version;
229
230 version = zimage_get_kernel_version(map_sysmem(setup, 0),
231 map_sysmem(kern_base, 0));
232 log_debug("version %s\n", version);
233 if (version)
234 bflow->name = strdup(version);
235#endif
236 if (!bflow->name)
237 bflow->name = strdup("ChromeOS");
238 if (!bflow->name)
239 return log_msg_ret("nam", -ENOMEM);
240 bflow->os_name = strdup("ChromeOS");
241 if (!bflow->os_name)
242 return log_msg_ret("os", -ENOMEM);
243
244 ret = copy_cmdline(map_sysmem(cmdline, 0), uuid, &bflow->cmdline);
245 if (ret)
246 return log_msg_ret("cmd", ret);
247 bflow->x86_setup = map_sysmem(setup, 0);
248
249 return 0;
250}
251
252/**
253 * cros_read_info() - Read information and fill out the bootflow
254 *
255 * @bflow: Bootflow to update
256 * @uuid: UUID string if supported, else NULL
257 * @preamble: Kernel preamble information
258 * Return: 0 if OK, -ENOMEM if out of memory, -EIO on read failure
259 */
260static int cros_read_info(struct bootflow *bflow, const char *uuid,
261 const struct vb2_kernel_preamble *preamble)
262{
263 struct cros_priv *priv = bflow->bootmeth_priv;
264 struct udevice *blk = bflow->blk;
265 struct blk_desc *desc = dev_get_uclass_plat(blk);
266 ulong offset, size, before_base;
267 void *buf;
268 int ret;
269
270 log_debug("Kernel preamble at %lx, version major %x, minor %x\n",
271 (ulong)map_to_sysmem(preamble),
272 preamble->header_version_major,
273 preamble->header_version_minor);
274
275 log_debug(" - load_address %lx, bl_addr %lx, bl_size %lx\n",
276 (ulong)preamble->body_load_address,
277 (ulong)preamble->bootloader_address,
278 (ulong)preamble->bootloader_size);
279
280 priv->body_size = preamble->body_signature.data_size;
281 priv->body_load_address = preamble->body_load_address;
282 priv->bootloader_address = preamble->bootloader_address;
283 priv->bootloader_size = preamble->bootloader_size;
284 log_debug("Kernel body at %lx size %lx\n", priv->body_offset,
285 priv->body_size);
286
287 /* Work out how many bytes to read before the bootloader base */
288 before_base = -CMDLINE_OFFSET;
289
290 /* Read the cmdline through to the end of the bootloader */
291 size = priv->bootloader_size + before_base;
292 offset = priv->body_offset +
293 (priv->bootloader_address - priv->body_load_address) +
294 CMDLINE_OFFSET;
295 buf = malloc(size);
296 if (!buf)
297 return log_msg_ret("buf", -ENOMEM);
298
299 ret = cros_read_buf(bflow, buf, size,
300 priv->part_start + (offset >> desc->log2blksz),
301 before_base, uuid);
302 if (ret) {
303 /* Clear this since the buffer is invalid */
304 bflow->x86_setup = NULL;
305 free(buf);
306 return log_msg_ret("pro", ret);
307 }
308 priv->info_buf = buf;
309
310 return 0;
311}
312
Simon Glass074503c2023-07-30 11:16:58 -0600313static int cros_read_kernel(struct bootflow *bflow)
314{
315 struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
316 struct cros_priv *priv = bflow->bootmeth_priv;
317 ulong base, setup;
318 ulong num_blks;
319 void *buf;
320 int ret;
321
322 bflow->size = priv->body_size;
323
324 buf = memalign(SZ_1K, priv->body_size);
325 if (!buf)
326 return log_msg_ret("buf", -ENOMEM);
327
328 /* Check that the header is not smaller than permitted */
329 if (priv->body_offset < PROBE_SIZE)
330 return log_msg_ret("san", EFAULT);
331
332 /* Read kernel body */
333 num_blks = priv->body_size >> desc->log2blksz;
334 log_debug("Reading body to %lx, blk=%s, size=%lx, blocks=%lx\n",
335 (ulong)map_to_sysmem(buf), bflow->blk->name, priv->body_size,
336 num_blks);
337 ret = blk_read(bflow->blk,
338 priv->part_start + (priv->body_offset >> desc->log2blksz),
339 num_blks, buf);
340 if (ret != num_blks)
341 return log_msg_ret("inf", -EIO);
342 base = map_to_sysmem(buf) + priv->bootloader_address -
343 priv->body_load_address;
344 setup = base + X86_SETUP_OFFSET;
345
346 bflow->buf = buf;
347 bflow->x86_setup = map_sysmem(setup, 0);
348
349 return 0;
350}
351
Simon Glassc88d67d2023-07-12 09:04:45 -0600352static int cros_read_bootflow(struct udevice *dev, struct bootflow *bflow)
353{
Simon Glass32578352023-07-30 11:16:51 -0600354 const struct vb2_kernel_preamble *preamble;
Simon Glassc88d67d2023-07-12 09:04:45 -0600355 struct disk_partition info;
Simon Glass32578352023-07-30 11:16:51 -0600356 struct vb2_keyblock *hdr;
Simon Glass598dea92023-07-30 11:16:59 -0600357 const char *uuid = NULL;
358 struct cros_priv *priv;
Simon Glass71f634b2023-08-24 13:55:45 -0600359 int ret;
Simon Glassc88d67d2023-07-12 09:04:45 -0600360
Simon Glass71f634b2023-08-24 13:55:45 -0600361 log_debug("starting, part=%x\n", bflow->part);
Simon Glassc88d67d2023-07-12 09:04:45 -0600362
Simon Glass71f634b2023-08-24 13:55:45 -0600363 /* Check for kernel partitions */
364 ret = scan_part(bflow->blk, bflow->part, &info, &hdr);
Simon Glass4cfe4512023-07-30 11:16:50 -0600365 if (ret) {
Simon Glass71f634b2023-08-24 13:55:45 -0600366 log_debug("- scan failed: err=%d\n", ret);
367 return log_msg_ret("scan", ret);
Simon Glass4cfe4512023-07-30 11:16:50 -0600368 }
Simon Glassc88d67d2023-07-12 09:04:45 -0600369
Simon Glass598dea92023-07-30 11:16:59 -0600370 priv = malloc(sizeof(struct cros_priv));
371 if (!priv) {
372 free(hdr);
373 return log_msg_ret("buf", -ENOMEM);
374 }
375 bflow->bootmeth_priv = priv;
376
Simon Glass71f634b2023-08-24 13:55:45 -0600377 log_debug("Selected partition %d, header at %lx\n", bflow->part,
378 (ulong)map_to_sysmem(hdr));
Simon Glass598dea92023-07-30 11:16:59 -0600379
380 /* Grab a few things from the preamble */
Simon Glass32578352023-07-30 11:16:51 -0600381 preamble = (void *)hdr + hdr->keyblock_size;
Simon Glass1d4bbdf2023-07-30 11:16:55 -0600382 priv->body_offset = hdr->keyblock_size + preamble->preamble_size;
383 priv->part_start = info.start;
Simon Glass32578352023-07-30 11:16:51 -0600384
Simon Glass598dea92023-07-30 11:16:59 -0600385 /* Now read everything we can learn about kernel */
Simon Glassc88d67d2023-07-12 09:04:45 -0600386#if CONFIG_IS_ENABLED(PARTITION_UUIDS)
387 uuid = info.uuid;
388#endif
Simon Glass598dea92023-07-30 11:16:59 -0600389 ret = cros_read_info(bflow, uuid, preamble);
390 preamble = NULL;
391 free(hdr);
Simon Glass71f634b2023-08-24 13:55:45 -0600392 if (ret) {
393 free(priv->info_buf);
394 free(priv);
Simon Glass598dea92023-07-30 11:16:59 -0600395 return log_msg_ret("inf", ret);
Simon Glass71f634b2023-08-24 13:55:45 -0600396 }
Simon Glass598dea92023-07-30 11:16:59 -0600397 bflow->size = priv->body_size;
Simon Glassc88d67d2023-07-12 09:04:45 -0600398 bflow->state = BOOTFLOWST_READY;
Simon Glassc88d67d2023-07-12 09:04:45 -0600399
400 return 0;
401}
402
403static int cros_read_file(struct udevice *dev, struct bootflow *bflow,
404 const char *file_path, ulong addr, ulong *sizep)
405{
406 return -ENOSYS;
407}
408
Simon Glassc2792242023-08-10 19:33:18 -0600409#if CONFIG_IS_ENABLED(BOOSTD_FULL)
410static int cros_read_all(struct udevice *dev, struct bootflow *bflow)
411{
412 int ret;
413
414 if (bflow->buf)
415 return log_msg_ret("ld", -EALREADY);
416 ret = cros_read_kernel(bflow);
417 if (ret)
418 return log_msg_ret("rd", ret);
419
420 return 0;
421}
422#endif /* BOOSTD_FULL */
423
Simon Glassc88d67d2023-07-12 09:04:45 -0600424static int cros_boot(struct udevice *dev, struct bootflow *bflow)
425{
Simon Glass598dea92023-07-30 11:16:59 -0600426 int ret;
427
Simon Glassc2792242023-08-10 19:33:18 -0600428 if (!bflow->buf) {
429 ret = cros_read_kernel(bflow);
430 if (ret)
431 return log_msg_ret("rd", ret);
432 }
Simon Glassc88d67d2023-07-12 09:04:45 -0600433
Simon Glassdaffb0b2023-07-30 11:17:02 -0600434 if (IS_ENABLED(CONFIG_X86)) {
435 ret = zboot_start(map_to_sysmem(bflow->buf), bflow->size, 0, 0,
436 map_to_sysmem(bflow->x86_setup),
437 bflow->cmdline);
438 } else {
439 ret = bootm_boot_start(map_to_sysmem(bflow->buf),
440 bflow->cmdline);
441 }
442
443 return log_msg_ret("go", ret);
Simon Glassc88d67d2023-07-12 09:04:45 -0600444}
445
446static int cros_bootmeth_bind(struct udevice *dev)
447{
448 struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
449
450 plat->desc = "ChromiumOS boot";
Simon Glass71f634b2023-08-24 13:55:45 -0600451 plat->flags = BOOTMETHF_ANY_PART;
Simon Glassc88d67d2023-07-12 09:04:45 -0600452
453 return 0;
454}
455
456static struct bootmeth_ops cros_bootmeth_ops = {
457 .check = cros_check,
458 .read_bootflow = cros_read_bootflow,
459 .read_file = cros_read_file,
460 .boot = cros_boot,
Simon Glassc2792242023-08-10 19:33:18 -0600461#if CONFIG_IS_ENABLED(BOOSTD_FULL)
462 .read_all = cros_read_all,
463#endif /* BOOSTD_FULL */
Simon Glassc88d67d2023-07-12 09:04:45 -0600464};
465
466static const struct udevice_id cros_bootmeth_ids[] = {
467 { .compatible = "u-boot,cros" },
468 { }
469};
470
471U_BOOT_DRIVER(bootmeth_cros) = {
472 .name = "bootmeth_cros",
473 .id = UCLASS_BOOTMETH,
474 .of_match = cros_bootmeth_ids,
475 .ops = &cros_bootmeth_ops,
476 .bind = cros_bootmeth_bind,
477};