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