Prepare v2023.10
[platform/kernel/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 <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>
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
25 enum {
26         /* Offsets in the kernel-partition header */
27         KERN_START      = 0x4f0,
28         KERN_SIZE       = 0x518,
29
30         SETUP_OFFSET    = 0x1000,       /* bytes before base */
31         CMDLINE_OFFSET  = 0x2000,       /* bytes before base */
32         OFFSET_BASE     = 0x100000,     /* assumed kernel load-address */
33 };
34
35 static 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
44 static 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
80 static int cros_read_bootflow(struct udevice *dev, struct bootflow *bflow)
81 {
82         struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
83         ulong base, start, size, setup, cmdline, num_blks, kern_base;
84         struct disk_partition info;
85         const char *uuid = NULL;
86         void *buf, *hdr;
87         int ret;
88
89         log_debug("starting, part=%d\n", bflow->part);
90
91         /* We consider the whole disk, not any one partition */
92         if (bflow->part)
93                 return log_msg_ret("max", -ENOENT);
94
95         /* Check partition 2 */
96         ret = part_get_info(desc, 2, &info);
97         if (ret)
98                 return log_msg_ret("part", ret);
99
100         /* Make a buffer for the header information */
101         num_blks = SZ_4K >> desc->log2blksz;
102         log_debug("Reading header, blk=%s, start=%lx, blocks=%lx\n",
103                   bflow->blk->name, (ulong)info.start, num_blks);
104         hdr = memalign(SZ_1K, SZ_4K);
105         if (!hdr)
106                 return log_msg_ret("hdr", -ENOMEM);
107         ret = blk_read(bflow->blk, info.start, num_blks, hdr);
108         if (ret != num_blks)
109                 return log_msg_ret("inf", ret);
110
111         if (memcmp("CHROMEOS", hdr, 8))
112                 return -ENOENT;
113
114         log_info("Header at %lx\n", (ulong)map_to_sysmem(hdr));
115         start = *(u32 *)(hdr + KERN_START);
116         size = ALIGN(*(u32 *)(hdr + KERN_SIZE), desc->blksz);
117         log_debug("Reading start %lx size %lx\n", start, size);
118         bflow->size = size;
119
120         buf = memalign(SZ_1K, size);
121         if (!buf)
122                 return log_msg_ret("buf", -ENOMEM);
123         num_blks = size >> desc->log2blksz;
124         log_debug("Reading data, blk=%s, start=%lx, blocks=%lx\n",
125                   bflow->blk->name, (ulong)info.start, num_blks);
126         ret = blk_read(bflow->blk, (ulong)info.start + 0x80, num_blks, buf);
127         if (ret != num_blks)
128                 return log_msg_ret("inf", ret);
129         base = map_to_sysmem(buf);
130
131         setup = base + start - OFFSET_BASE - SETUP_OFFSET;
132         cmdline = base + start - OFFSET_BASE - CMDLINE_OFFSET;
133         kern_base = base + start - OFFSET_BASE + SZ_16K;
134         log_debug("base %lx setup %lx, cmdline %lx, kern_base %lx\n", base,
135                   setup, cmdline, kern_base);
136
137 #ifdef CONFIG_X86
138         const char *version;
139
140         version = zimage_get_kernel_version(map_sysmem(setup, 0),
141                                             map_sysmem(kern_base, 0));
142         log_debug("version %s\n", version);
143         if (version)
144                 bflow->name = strdup(version);
145 #endif
146         if (!bflow->name)
147                 bflow->name = strdup("ChromeOS");
148         if (!bflow->name)
149                 return log_msg_ret("nam", -ENOMEM);
150         bflow->os_name = strdup("ChromeOS");
151         if (!bflow->os_name)
152                 return log_msg_ret("os", -ENOMEM);
153
154 #if CONFIG_IS_ENABLED(PARTITION_UUIDS)
155         uuid = info.uuid;
156 #endif
157         ret = copy_cmdline(map_sysmem(cmdline, 0), uuid, &bflow->cmdline);
158         if (ret)
159                 return log_msg_ret("cmd", ret);
160
161         bflow->state = BOOTFLOWST_READY;
162         bflow->buf = buf;
163         bflow->x86_setup = map_sysmem(setup, 0);
164
165         return 0;
166 }
167
168 static int cros_read_file(struct udevice *dev, struct bootflow *bflow,
169                          const char *file_path, ulong addr, ulong *sizep)
170 {
171         return -ENOSYS;
172 }
173
174 static int cros_boot(struct udevice *dev, struct bootflow *bflow)
175 {
176 #ifdef CONFIG_X86
177         zboot_start(map_to_sysmem(bflow->buf), bflow->size, 0, 0,
178                     map_to_sysmem(bflow->x86_setup),
179                     bflow->cmdline);
180 #endif
181
182         return log_msg_ret("go", -EFAULT);
183 }
184
185 static int cros_bootmeth_bind(struct udevice *dev)
186 {
187         struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
188
189         plat->desc = "ChromiumOS boot";
190
191         return 0;
192 }
193
194 static struct bootmeth_ops cros_bootmeth_ops = {
195         .check          = cros_check,
196         .read_bootflow  = cros_read_bootflow,
197         .read_file      = cros_read_file,
198         .boot           = cros_boot,
199 };
200
201 static const struct udevice_id cros_bootmeth_ids[] = {
202         { .compatible = "u-boot,cros" },
203         { }
204 };
205
206 U_BOOT_DRIVER(bootmeth_cros) = {
207         .name           = "bootmeth_cros",
208         .id             = UCLASS_BOOTMETH,
209         .of_match       = cros_bootmeth_ids,
210         .ops            = &cros_bootmeth_ops,
211         .bind           = cros_bootmeth_bind,
212 };