]> Git Repo - J-u-boot.git/blob - boot/bootmeth_cros.c
Merge patch series "Enable AVS support for AM68, AM69 and J784S4"
[J-u-boot.git] / boot / bootmeth_cros.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Bootmethod for ChromiumOS
4  *
5  * Copyright 2023 Google LLC
6  * Written by Simon Glass <[email protected]>
7  */
8
9 #define LOG_CATEGORY UCLASS_BOOTSTD
10
11 #include <blk.h>
12 #include <bootdev.h>
13 #include <bootflow.h>
14 #include <bootm.h>
15 #include <bootmeth.h>
16 #include <display_options.h>
17 #include <dm.h>
18 #include <efi.h>
19 #include <malloc.h>
20 #include <mapmem.h>
21 #include <part.h>
22 #include <linux/sizes.h>
23 #include "bootmeth_cros.h"
24
25 static const efi_guid_t cros_kern_type = PARTITION_CROS_KERNEL;
26
27 /*
28  * Layout of the ChromeOS kernel
29  *
30  * Partitions 2 and 4 contain kernels with type GUID_CROS_KERNEL
31  *
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
56 enum {
57         PROBE_SIZE      = SZ_4K,        /* initial bytes read from partition */
58
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 */
62 };
63
64 /**
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
77  * @info_buf: Buffer containing ChromiumOS info
78  */
79 struct 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;
86         void *info_buf;
87 };
88
89 static 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
98 static 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
134 /**
135  * scan_part() - Scan a kernel partition to see if has a ChromeOS header
136  *
137  * This reads the first PROBE_SIZE of a partition, loookng for
138  * VB2_KEYBLOCK_MAGIC
139  *
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  */
145 static int scan_part(struct udevice *blk, int partnum,
146                      struct disk_partition *info, struct vb2_keyblock **hdrp)
147 {
148         struct blk_desc *desc = dev_get_uclass_plat(blk);
149         struct vb2_keyblock *hdr;
150         efi_guid_t type;
151         ulong num_blks;
152         int ret;
153
154         if (!partnum)
155                 return log_msg_ret("efi", -ENOENT);
156
157         ret = part_get_info(desc, partnum, info);
158         if (ret)
159                 return log_msg_ret("part", ret);
160
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);
165
166         if (guidcmp(&cros_kern_type, &type))
167                 return log_msg_ret("typ", -ENOEXEC);
168
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);
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
182         if (memcmp(VB2_KEYBLOCK_MAGIC, hdr->magic, VB2_KEYBLOCK_MAGIC_SIZE)) {
183                 free(hdr);
184                 log_debug("no magic\n");
185                 return -ENOENT;
186         }
187
188         *hdrp = hdr;
189
190         return 0;
191 }
192
193 /**
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  */
204 static 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);
246         bflow->x86_setup = map_sysmem(setup, 0);
247
248         return 0;
249 }
250
251 /**
252  * cros_read_info() - Read information and fill out the bootflow
253  *
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
258  */
259 static int cros_read_info(struct bootflow *bflow, const char *uuid,
260                           const struct vb2_kernel_preamble *preamble)
261 {
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;
266         void *buf;
267         int ret;
268
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);
273
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);
278
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,
284                   priv->body_size);
285
286         /* Work out how many bytes to read before the bootloader base */
287         before_base = -CMDLINE_OFFSET;
288
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) +
293                 CMDLINE_OFFSET;
294         buf = malloc(size);
295         if (!buf)
296                 return log_msg_ret("buf", -ENOMEM);
297
298         ret = cros_read_buf(bflow, buf, size,
299                             priv->part_start + (offset >> desc->log2blksz),
300                             before_base, uuid);
301         if (ret) {
302                 /* Clear this since the buffer is invalid */
303                 bflow->x86_setup = NULL;
304                 free(buf);
305                 return log_msg_ret("pro", ret);
306         }
307         priv->info_buf = buf;
308
309         return 0;
310 }
311
312 static int cros_read_kernel(struct bootflow *bflow)
313 {
314         struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
315         struct cros_priv *priv = bflow->bootmeth_priv;
316         ulong base, setup;
317         ulong num_blks;
318         void *buf;
319         int ret;
320
321         bflow->size = priv->body_size;
322
323         buf = memalign(SZ_1K, priv->body_size);
324         if (!buf)
325                 return log_msg_ret("buf", -ENOMEM);
326
327         /* Check that the header is not smaller than permitted */
328         if (priv->body_offset < PROBE_SIZE)
329                 return log_msg_ret("san", EFAULT);
330
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,
335                   num_blks);
336         ret = blk_read(bflow->blk,
337                        priv->part_start + (priv->body_offset >> desc->log2blksz),
338                        num_blks, buf);
339         if (ret != num_blks)
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;
344
345         bflow->buf = buf;
346         bflow->x86_setup = map_sysmem(setup, 0);
347
348         return 0;
349 }
350
351 static int cros_read_bootflow(struct udevice *dev, struct bootflow *bflow)
352 {
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;
358         int ret;
359
360         log_debug("starting, part=%x\n", bflow->part);
361
362         /* Check for kernel partitions */
363         ret = scan_part(bflow->blk, bflow->part, &info, &hdr);
364         if (ret) {
365                 log_debug("- scan failed: err=%d\n", ret);
366                 return log_msg_ret("scan", ret);
367         }
368
369         priv = malloc(sizeof(struct cros_priv));
370         if (!priv) {
371                 free(hdr);
372                 return log_msg_ret("buf", -ENOMEM);
373         }
374         bflow->bootmeth_priv = priv;
375
376         log_debug("Selected partition %d, header at %lx\n", bflow->part,
377                   (ulong)map_to_sysmem(hdr));
378
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;
383
384         /* Now read everything we can learn about kernel */
385 #if CONFIG_IS_ENABLED(PARTITION_UUIDS)
386         uuid = info.uuid;
387 #endif
388         ret = cros_read_info(bflow, uuid, preamble);
389         preamble = NULL;
390         free(hdr);
391         if (ret) {
392                 free(priv->info_buf);
393                 free(priv);
394                 return log_msg_ret("inf", ret);
395         }
396         bflow->size = priv->body_size;
397         bflow->state = BOOTFLOWST_READY;
398
399         return 0;
400 }
401
402 static int cros_read_file(struct udevice *dev, struct bootflow *bflow,
403                          const char *file_path, ulong addr, ulong *sizep)
404 {
405         return -ENOSYS;
406 }
407
408 #if CONFIG_IS_ENABLED(BOOTSTD_FULL)
409 static int cros_read_all(struct udevice *dev, struct bootflow *bflow)
410 {
411         int ret;
412
413         if (bflow->buf)
414                 return log_msg_ret("ld", -EALREADY);
415         ret = cros_read_kernel(bflow);
416         if (ret)
417                 return log_msg_ret("rd", ret);
418
419         return 0;
420 }
421 #endif /* BOOTSTD_FULL */
422
423 static int cros_boot(struct udevice *dev, struct bootflow *bflow)
424 {
425         int ret;
426
427         if (!bflow->buf) {
428                 ret = cros_read_kernel(bflow);
429                 if (ret)
430                         return log_msg_ret("rd", ret);
431         }
432
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),
436                                 bflow->cmdline);
437         } else {
438                 ret = bootm_boot_start(map_to_sysmem(bflow->buf),
439                                        bflow->cmdline);
440         }
441
442         return log_msg_ret("go", ret);
443 }
444
445 static int cros_bootmeth_bind(struct udevice *dev)
446 {
447         struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
448
449         plat->desc = "ChromiumOS boot";
450         plat->flags = BOOTMETHF_ANY_PART;
451
452         return 0;
453 }
454
455 static struct bootmeth_ops cros_bootmeth_ops = {
456         .check          = cros_check,
457         .read_bootflow  = cros_read_bootflow,
458         .read_file      = cros_read_file,
459         .boot           = cros_boot,
460 #if CONFIG_IS_ENABLED(BOOTSTD_FULL)
461         .read_all       = cros_read_all,
462 #endif /* BOOTSTD_FULL */
463 };
464
465 static const struct udevice_id cros_bootmeth_ids[] = {
466         { .compatible = "u-boot,cros" },
467         { }
468 };
469
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,
476 };
This page took 0.052203 seconds and 4 git commands to generate.