]> Git Repo - J-u-boot.git/blob - boot/bootmeth_cros.c
bootstd: cros: Bring in some ChromiumOS structures
[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 <common.h>
12 #include <blk.h>
13 #include <bootdev.h>
14 #include <bootflow.h>
15 #include <bootmeth.h>
16 #include <dm.h>
17 #include <malloc.h>
18 #include <mapmem.h>
19 #include <part.h>
20 #ifdef CONFIG_X86
21 #include <asm/zimage.h>
22 #endif
23 #include <linux/sizes.h>
24 #include "bootmeth_cros.h"
25
26 enum {
27         PROBE_SIZE      = SZ_4K,        /* initial bytes read from partition */
28
29         /* Offsets in the kernel-partition header */
30         KERN_START      = 0x4f0,
31         KERN_SIZE       = 0x518,
32
33         SETUP_OFFSET    = 0x1000,       /* bytes before base */
34         CMDLINE_OFFSET  = 0x2000,       /* bytes before base */
35         OFFSET_BASE     = 0x100000,     /* assumed kernel load-address */
36 };
37
38 static int cros_check(struct udevice *dev, struct bootflow_iter *iter)
39 {
40         /* This only works on block and network devices */
41         if (bootflow_iter_check_blk(iter))
42                 return log_msg_ret("blk", -ENOTSUPP);
43
44         return 0;
45 }
46
47 static int copy_cmdline(const char *from, const char *uuid, char **bufp)
48 {
49         const int maxlen = 2048;
50         char buf[maxlen];
51         char *cmd, *to, *end;
52         int len;
53
54         /* Allow space for cmdline + UUID */
55         len = strnlen(from, sizeof(buf));
56         if (len >= maxlen)
57                 return -E2BIG;
58
59         log_debug("uuid %d %s\n", uuid ? (int)strlen(uuid) : 0, uuid);
60         for (to = buf, end = buf + maxlen - UUID_STR_LEN - 1; *from; from++) {
61                 if (to >= end)
62                         return -E2BIG;
63                 if (from[0] == '%' && from[1] == 'U' && uuid &&
64                     strlen(uuid) == UUID_STR_LEN) {
65                         strcpy(to, uuid);
66                         to += UUID_STR_LEN;
67                         from++;
68                 } else {
69                         *to++ = *from;
70                 }
71         }
72         *to = '\0';
73         len = to - buf;
74         cmd = strdup(buf);
75         if (!cmd)
76                 return -ENOMEM;
77         free(*bufp);
78         *bufp = cmd;
79
80         return 0;
81 }
82
83 /**
84  * scan_part() - Scan a kernel partition to see if has a ChromeOS header
85  *
86  * This reads the first PROBE_SIZE of a partition, loookng for
87  * VB2_KEYBLOCK_MAGIC
88  *
89  * @blk: Block device to scan
90  * @partnum: Partition number to scan
91  * @info: Please to put partition info
92  * @hdrp: Return allocated keyblock header on success
93  */
94 static int scan_part(struct udevice *blk, int partnum,
95                      struct disk_partition *info, void **hdrp)
96 {
97         struct blk_desc *desc = dev_get_uclass_plat(blk);
98         struct vb2_keyblock *hdr;
99         ulong num_blks;
100         int ret;
101
102         ret = part_get_info(desc, partnum, info);
103         if (ret)
104                 return log_msg_ret("part", ret);
105
106         /* Make a buffer for the header information */
107         num_blks = PROBE_SIZE >> desc->log2blksz;
108         log_debug("Reading header, blk=%s, start=%lx, blocks=%lx\n",
109                   blk->name, (ulong)info->start, num_blks);
110         hdr = memalign(SZ_1K, PROBE_SIZE);
111         if (!hdr)
112                 return log_msg_ret("hdr", -ENOMEM);
113         ret = blk_read(blk, info->start, num_blks, hdr);
114         if (ret != num_blks) {
115                 free(hdr);
116                 return log_msg_ret("inf", -EIO);
117         }
118
119         if (memcmp(VB2_KEYBLOCK_MAGIC, hdr->magic, VB2_KEYBLOCK_MAGIC_SIZE)) {
120                 free(hdr);
121                 return -ENOENT;
122         }
123
124         *hdrp = hdr;
125
126         return 0;
127 }
128
129 static int cros_read_bootflow(struct udevice *dev, struct bootflow *bflow)
130 {
131         struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
132         ulong base, start, size, setup, cmdline, num_blks, kern_base;
133         struct disk_partition info;
134         const char *uuid = NULL;
135         void *buf, *hdr;
136         int ret;
137
138         log_debug("starting, part=%d\n", bflow->part);
139
140         /* We consider the whole disk, not any one partition */
141         if (bflow->part)
142                 return log_msg_ret("max", -ENOENT);
143
144         /* Check partition 2 */
145         ret = scan_part(bflow->blk, 2, &info, &hdr);
146         if (ret)
147                 return log_msg_ret("scan", ret);
148         bflow->part = 2;
149
150         log_info("Header at %lx\n", (ulong)map_to_sysmem(hdr));
151         start = *(u32 *)(hdr + KERN_START);
152         size = ALIGN(*(u32 *)(hdr + KERN_SIZE), desc->blksz);
153         log_debug("Reading start %lx size %lx\n", start, size);
154         bflow->size = size;
155
156         buf = memalign(SZ_1K, size);
157         if (!buf)
158                 return log_msg_ret("buf", -ENOMEM);
159         num_blks = size >> desc->log2blksz;
160         log_debug("Reading data, blk=%s, start=%lx, blocks=%lx\n",
161                   bflow->blk->name, (ulong)info.start, num_blks);
162         ret = blk_read(bflow->blk, (ulong)info.start + 0x80, num_blks, buf);
163         if (ret != num_blks)
164                 return log_msg_ret("inf", -EIO);
165         base = map_to_sysmem(buf);
166
167         setup = base + start - OFFSET_BASE - SETUP_OFFSET;
168         cmdline = base + start - OFFSET_BASE - CMDLINE_OFFSET;
169         kern_base = base + start - OFFSET_BASE + SZ_16K;
170         log_debug("base %lx setup %lx, cmdline %lx, kern_base %lx\n", base,
171                   setup, cmdline, kern_base);
172
173 #ifdef CONFIG_X86
174         const char *version;
175
176         version = zimage_get_kernel_version(map_sysmem(setup, 0),
177                                             map_sysmem(kern_base, 0));
178         log_debug("version %s\n", version);
179         if (version)
180                 bflow->name = strdup(version);
181 #endif
182         if (!bflow->name)
183                 bflow->name = strdup("ChromeOS");
184         if (!bflow->name)
185                 return log_msg_ret("nam", -ENOMEM);
186         bflow->os_name = strdup("ChromeOS");
187         if (!bflow->os_name)
188                 return log_msg_ret("os", -ENOMEM);
189
190 #if CONFIG_IS_ENABLED(PARTITION_UUIDS)
191         uuid = info.uuid;
192 #endif
193         ret = copy_cmdline(map_sysmem(cmdline, 0), uuid, &bflow->cmdline);
194         if (ret)
195                 return log_msg_ret("cmd", ret);
196
197         bflow->state = BOOTFLOWST_READY;
198         bflow->buf = buf;
199         bflow->x86_setup = map_sysmem(setup, 0);
200
201         return 0;
202 }
203
204 static int cros_read_file(struct udevice *dev, struct bootflow *bflow,
205                          const char *file_path, ulong addr, ulong *sizep)
206 {
207         return -ENOSYS;
208 }
209
210 static int cros_boot(struct udevice *dev, struct bootflow *bflow)
211 {
212 #ifdef CONFIG_X86
213         zboot_start(map_to_sysmem(bflow->buf), bflow->size, 0, 0,
214                     map_to_sysmem(bflow->x86_setup),
215                     bflow->cmdline);
216 #endif
217
218         return log_msg_ret("go", -EFAULT);
219 }
220
221 static int cros_bootmeth_bind(struct udevice *dev)
222 {
223         struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
224
225         plat->desc = "ChromiumOS boot";
226
227         return 0;
228 }
229
230 static struct bootmeth_ops cros_bootmeth_ops = {
231         .check          = cros_check,
232         .read_bootflow  = cros_read_bootflow,
233         .read_file      = cros_read_file,
234         .boot           = cros_boot,
235 };
236
237 static const struct udevice_id cros_bootmeth_ids[] = {
238         { .compatible = "u-boot,cros" },
239         { }
240 };
241
242 U_BOOT_DRIVER(bootmeth_cros) = {
243         .name           = "bootmeth_cros",
244         .id             = UCLASS_BOOTMETH,
245         .of_match       = cros_bootmeth_ids,
246         .ops            = &cros_bootmeth_ops,
247         .bind           = cros_bootmeth_bind,
248 };
This page took 0.038072 seconds and 4 git commands to generate.