Merge tag 'u-boot-at91-fixes-2023.01-a' of https://source.denx.de/u-boot/custodians...
[platform/kernel/u-boot.git] / boot / bootmeth_distro.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Bootmethod for distro boot (syslinux boot from a block device)
4  *
5  * Copyright 2021 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 <bootdev.h>
13 #include <bootflow.h>
14 #include <bootmeth.h>
15 #include <bootstd.h>
16 #include <command.h>
17 #include <distro.h>
18 #include <dm.h>
19 #include <fs.h>
20 #include <malloc.h>
21 #include <mapmem.h>
22 #include <mmc.h>
23 #include <pxe_utils.h>
24
25 static int distro_get_state_desc(struct udevice *dev, char *buf, int maxsize)
26 {
27         if (IS_ENABLED(CONFIG_SANDBOX)) {
28                 int len;
29
30                 len = snprintf(buf, maxsize, "OK");
31
32                 return len + 1 < maxsize ? 0 : -ENOSPC;
33         }
34
35         return 0;
36 }
37
38 static int distro_getfile(struct pxe_context *ctx, const char *file_path,
39                           char *file_addr, ulong *sizep)
40 {
41         struct distro_info *info = ctx->userdata;
42         ulong addr;
43         int ret;
44
45         addr = simple_strtoul(file_addr, NULL, 16);
46
47         /* Allow up to 1GB */
48         *sizep = 1 << 30;
49         ret = bootmeth_read_file(info->dev, info->bflow, file_path, addr,
50                                  sizep);
51         if (ret)
52                 return log_msg_ret("read", ret);
53
54         return 0;
55 }
56
57 static int distro_check(struct udevice *dev, struct bootflow_iter *iter)
58 {
59         int ret;
60
61         /* This only works on block devices */
62         ret = bootflow_iter_uses_blk_dev(iter);
63         if (ret)
64                 return log_msg_ret("blk", ret);
65
66         return 0;
67 }
68
69 static int distro_read_bootflow(struct udevice *dev, struct bootflow *bflow)
70 {
71         struct blk_desc *desc;
72         const char *const *prefixes;
73         struct udevice *bootstd;
74         const char *prefix;
75         loff_t size;
76         int ret, i;
77
78         ret = uclass_first_device_err(UCLASS_BOOTSTD, &bootstd);
79         if (ret)
80                 return log_msg_ret("std", ret);
81
82         /* If a block device, we require a partition table */
83         if (bflow->blk && !bflow->part)
84                 return -ENOENT;
85
86         prefixes = bootstd_get_prefixes(bootstd);
87         i = 0;
88         desc = bflow->blk ? dev_get_uclass_plat(bflow->blk) : NULL;
89         do {
90                 prefix = prefixes ? prefixes[i] : NULL;
91
92                 ret = bootmeth_try_file(bflow, desc, prefix, DISTRO_FNAME);
93         } while (ret && prefixes && prefixes[++i]);
94         if (ret)
95                 return log_msg_ret("try", ret);
96         size = bflow->size;
97
98         ret = bootmeth_alloc_file(bflow, 0x10000, 1);
99         if (ret)
100                 return log_msg_ret("read", ret);
101
102         return 0;
103 }
104
105 static int distro_boot(struct udevice *dev, struct bootflow *bflow)
106 {
107         struct cmd_tbl cmdtp = {};      /* dummy */
108         struct pxe_context ctx;
109         struct distro_info info;
110         ulong addr;
111         int ret;
112
113         addr = map_to_sysmem(bflow->buf);
114         info.dev = dev;
115         info.bflow = bflow;
116         ret = pxe_setup_ctx(&ctx, &cmdtp, distro_getfile, &info, true,
117                             bflow->subdir);
118         if (ret)
119                 return log_msg_ret("ctx", -EINVAL);
120
121         ret = pxe_process(&ctx, addr, false);
122         if (ret)
123                 return log_msg_ret("bread", -EINVAL);
124
125         return 0;
126 }
127
128 static int distro_bootmeth_bind(struct udevice *dev)
129 {
130         struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
131
132         plat->desc = IS_ENABLED(CONFIG_BOOTSTD_FULL) ?
133                 "Syslinux boot from a block device" : "syslinux";
134
135         return 0;
136 }
137
138 static struct bootmeth_ops distro_bootmeth_ops = {
139         .get_state_desc = distro_get_state_desc,
140         .check          = distro_check,
141         .read_bootflow  = distro_read_bootflow,
142         .read_file      = bootmeth_common_read_file,
143         .boot           = distro_boot,
144 };
145
146 static const struct udevice_id distro_bootmeth_ids[] = {
147         { .compatible = "u-boot,distro-syslinux" },
148         { }
149 };
150
151 U_BOOT_DRIVER(bootmeth_distro) = {
152         .name           = "bootmeth_distro",
153         .id             = UCLASS_BOOTMETH,
154         .of_match       = distro_bootmeth_ids,
155         .ops            = &distro_bootmeth_ops,
156         .bind           = distro_bootmeth_bind,
157 };