blob: 2a745621e30e86a97425a7462dc3ac99cbb371cd [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>
15#include <bootmeth.h>
Simon Glass4cfe4512023-07-30 11:16:50 -060016#include <display_options.h>
Simon Glassc88d67d2023-07-12 09:04:45 -060017#include <dm.h>
18#include <malloc.h>
19#include <mapmem.h>
20#include <part.h>
21#ifdef CONFIG_X86
22#include <asm/zimage.h>
23#endif
24#include <linux/sizes.h>
Simon Glassde30aa92023-07-30 11:16:49 -060025#include "bootmeth_cros.h"
Simon Glassc88d67d2023-07-12 09:04:45 -060026
27enum {
Simon Glass5a8589e2023-07-30 11:16:48 -060028 PROBE_SIZE = SZ_4K, /* initial bytes read from partition */
29
Simon Glassc88d67d2023-07-12 09:04:45 -060030 SETUP_OFFSET = 0x1000, /* bytes before base */
31 CMDLINE_OFFSET = 0x2000, /* bytes before base */
32 OFFSET_BASE = 0x100000, /* assumed kernel load-address */
33};
34
35static int cros_check(struct udevice *dev, struct bootflow_iter *iter)
36{
37 /* This only works on block and network devices */
38 if (bootflow_iter_check_blk(iter))
39 return log_msg_ret("blk", -ENOTSUPP);
40
41 return 0;
42}
43
44static int copy_cmdline(const char *from, const char *uuid, char **bufp)
45{
46 const int maxlen = 2048;
47 char buf[maxlen];
48 char *cmd, *to, *end;
49 int len;
50
51 /* Allow space for cmdline + UUID */
52 len = strnlen(from, sizeof(buf));
53 if (len >= maxlen)
54 return -E2BIG;
55
56 log_debug("uuid %d %s\n", uuid ? (int)strlen(uuid) : 0, uuid);
57 for (to = buf, end = buf + maxlen - UUID_STR_LEN - 1; *from; from++) {
58 if (to >= end)
59 return -E2BIG;
60 if (from[0] == '%' && from[1] == 'U' && uuid &&
61 strlen(uuid) == UUID_STR_LEN) {
62 strcpy(to, uuid);
63 to += UUID_STR_LEN;
64 from++;
65 } else {
66 *to++ = *from;
67 }
68 }
69 *to = '\0';
70 len = to - buf;
71 cmd = strdup(buf);
72 if (!cmd)
73 return -ENOMEM;
74 free(*bufp);
75 *bufp = cmd;
76
77 return 0;
78}
79
Simon Glass5a8589e2023-07-30 11:16:48 -060080/**
81 * scan_part() - Scan a kernel partition to see if has a ChromeOS header
82 *
Simon Glassde30aa92023-07-30 11:16:49 -060083 * This reads the first PROBE_SIZE of a partition, loookng for
84 * VB2_KEYBLOCK_MAGIC
Simon Glass5a8589e2023-07-30 11:16:48 -060085 *
86 * @blk: Block device to scan
87 * @partnum: Partition number to scan
88 * @info: Please to put partition info
89 * @hdrp: Return allocated keyblock header on success
90 */
91static int scan_part(struct udevice *blk, int partnum,
Simon Glass32578352023-07-30 11:16:51 -060092 struct disk_partition *info, struct vb2_keyblock **hdrp)
Simon Glass5a8589e2023-07-30 11:16:48 -060093{
94 struct blk_desc *desc = dev_get_uclass_plat(blk);
95 struct vb2_keyblock *hdr;
96 ulong num_blks;
97 int ret;
98
99 ret = part_get_info(desc, partnum, info);
100 if (ret)
101 return log_msg_ret("part", ret);
102
103 /* Make a buffer for the header information */
104 num_blks = PROBE_SIZE >> desc->log2blksz;
105 log_debug("Reading header, blk=%s, start=%lx, blocks=%lx\n",
106 blk->name, (ulong)info->start, num_blks);
107 hdr = memalign(SZ_1K, PROBE_SIZE);
108 if (!hdr)
109 return log_msg_ret("hdr", -ENOMEM);
110 ret = blk_read(blk, info->start, num_blks, hdr);
111 if (ret != num_blks) {
112 free(hdr);
113 return log_msg_ret("inf", -EIO);
114 }
115
Simon Glassde30aa92023-07-30 11:16:49 -0600116 if (memcmp(VB2_KEYBLOCK_MAGIC, hdr->magic, VB2_KEYBLOCK_MAGIC_SIZE)) {
Simon Glass5a8589e2023-07-30 11:16:48 -0600117 free(hdr);
118 return -ENOENT;
119 }
120
121 *hdrp = hdr;
122
123 return 0;
124}
125
Simon Glassc88d67d2023-07-12 09:04:45 -0600126static int cros_read_bootflow(struct udevice *dev, struct bootflow *bflow)
127{
128 struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
Simon Glass32578352023-07-30 11:16:51 -0600129 ulong base, start, setup, cmdline, num_blks, kern_base;
130 const struct vb2_kernel_preamble *preamble;
131 ulong body_offset, body_size;
Simon Glassc88d67d2023-07-12 09:04:45 -0600132 struct disk_partition info;
133 const char *uuid = NULL;
Simon Glass32578352023-07-30 11:16:51 -0600134 struct vb2_keyblock *hdr;
Simon Glass4cfe4512023-07-30 11:16:50 -0600135 int part, ret;
Simon Glass32578352023-07-30 11:16:51 -0600136 void *buf;
Simon Glassc88d67d2023-07-12 09:04:45 -0600137
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
Simon Glass4cfe4512023-07-30 11:16:50 -0600144 /* Check partition 2 then 4 */
145 part = 2;
146 ret = scan_part(bflow->blk, part, &info, &hdr);
147 if (ret) {
148 part = 4;
149 ret = scan_part(bflow->blk, part, &info, &hdr);
150 if (ret)
151 return log_msg_ret("scan", ret);
152 }
153 bflow->part = part;
Simon Glassc88d67d2023-07-12 09:04:45 -0600154
Simon Glass4cfe4512023-07-30 11:16:50 -0600155 log_info("Selected parition %d, header at %lx\n", bflow->part,
156 (ulong)map_to_sysmem(hdr));
Simon Glass32578352023-07-30 11:16:51 -0600157 preamble = (void *)hdr + hdr->keyblock_size;
158 log_debug("Kernel preamble at %lx, version major %x, minor %x\n",
159 (ulong)map_to_sysmem(preamble),
160 preamble->header_version_major,
161 preamble->header_version_minor);
Simon Glassc88d67d2023-07-12 09:04:45 -0600162
Simon Glass32578352023-07-30 11:16:51 -0600163 start = (ulong)preamble->bootloader_address;
164 log_debug(" - load_address %lx, bl_addr %lx, bl_size %lx\n",
165 (ulong)preamble->body_load_address,
166 (ulong)preamble->bootloader_address,
167 (ulong)preamble->bootloader_size);
168
169 body_offset = hdr->keyblock_size + preamble->preamble_size;
170 body_size = preamble->body_signature.data_size;
171 log_debug("Kernel body at %lx size %lx\n", body_offset, body_size);
172 bflow->size = body_size;
173
174 buf = memalign(SZ_1K, body_size);
Simon Glassc88d67d2023-07-12 09:04:45 -0600175 if (!buf)
176 return log_msg_ret("buf", -ENOMEM);
Simon Glass32578352023-07-30 11:16:51 -0600177
178 /* Check that the header is not smaller than permitted */
179 if (body_offset < PROBE_SIZE)
180 return log_msg_ret("san", EFAULT);
181
182 /* Read kernel body */
183 num_blks = body_size >> desc->log2blksz;
184 log_debug("Reading body to %lx, blk=%s, size=%lx, blocks=%lx\n",
185 (ulong)map_to_sysmem(buf), bflow->blk->name, body_size,
186 num_blks);
187 ret = blk_read(bflow->blk,
188 info.start + (body_offset >> desc->log2blksz),
189 num_blks, buf);
Simon Glassc88d67d2023-07-12 09:04:45 -0600190 if (ret != num_blks)
Simon Glassdefa33a2023-07-30 11:16:47 -0600191 return log_msg_ret("inf", -EIO);
Simon Glassc88d67d2023-07-12 09:04:45 -0600192 base = map_to_sysmem(buf);
193
194 setup = base + start - OFFSET_BASE - SETUP_OFFSET;
195 cmdline = base + start - OFFSET_BASE - CMDLINE_OFFSET;
196 kern_base = base + start - OFFSET_BASE + SZ_16K;
197 log_debug("base %lx setup %lx, cmdline %lx, kern_base %lx\n", base,
198 setup, cmdline, kern_base);
199
200#ifdef CONFIG_X86
201 const char *version;
202
203 version = zimage_get_kernel_version(map_sysmem(setup, 0),
204 map_sysmem(kern_base, 0));
205 log_debug("version %s\n", version);
206 if (version)
207 bflow->name = strdup(version);
208#endif
209 if (!bflow->name)
210 bflow->name = strdup("ChromeOS");
211 if (!bflow->name)
212 return log_msg_ret("nam", -ENOMEM);
213 bflow->os_name = strdup("ChromeOS");
214 if (!bflow->os_name)
215 return log_msg_ret("os", -ENOMEM);
216
217#if CONFIG_IS_ENABLED(PARTITION_UUIDS)
218 uuid = info.uuid;
219#endif
220 ret = copy_cmdline(map_sysmem(cmdline, 0), uuid, &bflow->cmdline);
221 if (ret)
222 return log_msg_ret("cmd", ret);
223
224 bflow->state = BOOTFLOWST_READY;
225 bflow->buf = buf;
226 bflow->x86_setup = map_sysmem(setup, 0);
227
228 return 0;
229}
230
231static int cros_read_file(struct udevice *dev, struct bootflow *bflow,
232 const char *file_path, ulong addr, ulong *sizep)
233{
234 return -ENOSYS;
235}
236
237static int cros_boot(struct udevice *dev, struct bootflow *bflow)
238{
239#ifdef CONFIG_X86
240 zboot_start(map_to_sysmem(bflow->buf), bflow->size, 0, 0,
241 map_to_sysmem(bflow->x86_setup),
242 bflow->cmdline);
243#endif
244
245 return log_msg_ret("go", -EFAULT);
246}
247
248static int cros_bootmeth_bind(struct udevice *dev)
249{
250 struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
251
252 plat->desc = "ChromiumOS boot";
253
254 return 0;
255}
256
257static struct bootmeth_ops cros_bootmeth_ops = {
258 .check = cros_check,
259 .read_bootflow = cros_read_bootflow,
260 .read_file = cros_read_file,
261 .boot = cros_boot,
262};
263
264static const struct udevice_id cros_bootmeth_ids[] = {
265 { .compatible = "u-boot,cros" },
266 { }
267};
268
269U_BOOT_DRIVER(bootmeth_cros) = {
270 .name = "bootmeth_cros",
271 .id = UCLASS_BOOTMETH,
272 .of_match = cros_bootmeth_ids,
273 .ops = &cros_bootmeth_ops,
274 .bind = cros_bootmeth_bind,
275};