]>
Commit | Line | Data |
---|---|---|
31aefaf8 SG |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
79f66351 | 3 | * Bootmethod for extlinux boot from a block device |
31aefaf8 SG |
4 | * |
5 | * Copyright 2021 Google LLC | |
6 | * Written by Simon Glass <[email protected]> | |
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> | |
31aefaf8 | 17 | #include <dm.h> |
79f66351 | 18 | #include <extlinux.h> |
31aefaf8 SG |
19 | #include <fs.h> |
20 | #include <malloc.h> | |
21 | #include <mapmem.h> | |
22 | #include <mmc.h> | |
23 | #include <pxe_utils.h> | |
24 | ||
79f66351 | 25 | static int extlinux_get_state_desc(struct udevice *dev, char *buf, int maxsize) |
988cacae SG |
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 | ||
79f66351 SG |
38 | static int extlinux_getfile(struct pxe_context *ctx, const char *file_path, |
39 | char *file_addr, ulong *sizep) | |
31aefaf8 | 40 | { |
79f66351 | 41 | struct extlinux_info *info = ctx->userdata; |
31aefaf8 SG |
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 | ||
79f66351 | 57 | static int extlinux_check(struct udevice *dev, struct bootflow_iter *iter) |
31aefaf8 SG |
58 | { |
59 | int ret; | |
60 | ||
61 | /* This only works on block devices */ | |
865328c3 | 62 | ret = bootflow_iter_check_blk(iter); |
31aefaf8 SG |
63 | if (ret) |
64 | return log_msg_ret("blk", ret); | |
65 | ||
66 | return 0; | |
67 | } | |
68 | ||
2175e76a | 69 | /** |
79f66351 | 70 | * extlinux_fill_info() - Decode the extlinux file to find out its info |
2175e76a SG |
71 | * |
72 | * @bflow: Bootflow to process | |
73 | * @return 0 if OK, -ve on error | |
74 | */ | |
79f66351 | 75 | static int extlinux_fill_info(struct bootflow *bflow) |
2175e76a SG |
76 | { |
77 | struct membuff mb; | |
78 | char line[200]; | |
79 | char *data; | |
80 | int len; | |
81 | ||
82 | log_debug("parsing bflow file size %x\n", bflow->size); | |
83 | membuff_init(&mb, bflow->buf, bflow->size); | |
84 | membuff_putraw(&mb, bflow->size, true, &data); | |
85 | while (len = membuff_readline(&mb, line, sizeof(line) - 1, ' '), len) { | |
86 | char *tok, *p = line; | |
87 | ||
88 | tok = strsep(&p, " "); | |
89 | if (p) { | |
90 | if (!strcmp("label", tok)) { | |
91 | bflow->os_name = strdup(p); | |
92 | if (!bflow->os_name) | |
93 | return log_msg_ret("os", -ENOMEM); | |
94 | } | |
95 | } | |
96 | } | |
97 | ||
98 | return 0; | |
99 | } | |
100 | ||
79f66351 | 101 | static int extlinux_read_bootflow(struct udevice *dev, struct bootflow *bflow) |
31aefaf8 SG |
102 | { |
103 | struct blk_desc *desc; | |
104 | const char *const *prefixes; | |
105 | struct udevice *bootstd; | |
106 | const char *prefix; | |
107 | loff_t size; | |
108 | int ret, i; | |
109 | ||
110 | ret = uclass_first_device_err(UCLASS_BOOTSTD, &bootstd); | |
111 | if (ret) | |
112 | return log_msg_ret("std", ret); | |
113 | ||
114 | /* If a block device, we require a partition table */ | |
115 | if (bflow->blk && !bflow->part) | |
116 | return -ENOENT; | |
117 | ||
118 | prefixes = bootstd_get_prefixes(bootstd); | |
119 | i = 0; | |
120 | desc = bflow->blk ? dev_get_uclass_plat(bflow->blk) : NULL; | |
121 | do { | |
122 | prefix = prefixes ? prefixes[i] : NULL; | |
123 | ||
79f66351 | 124 | ret = bootmeth_try_file(bflow, desc, prefix, EXTLINUX_FNAME); |
31aefaf8 SG |
125 | } while (ret && prefixes && prefixes[++i]); |
126 | if (ret) | |
127 | return log_msg_ret("try", ret); | |
128 | size = bflow->size; | |
129 | ||
130 | ret = bootmeth_alloc_file(bflow, 0x10000, 1); | |
131 | if (ret) | |
132 | return log_msg_ret("read", ret); | |
133 | ||
79f66351 | 134 | ret = extlinux_fill_info(bflow); |
2175e76a SG |
135 | if (ret) |
136 | return log_msg_ret("inf", ret); | |
137 | ||
31aefaf8 SG |
138 | return 0; |
139 | } | |
140 | ||
79f66351 | 141 | static int extlinux_boot(struct udevice *dev, struct bootflow *bflow) |
31aefaf8 SG |
142 | { |
143 | struct cmd_tbl cmdtp = {}; /* dummy */ | |
144 | struct pxe_context ctx; | |
79f66351 | 145 | struct extlinux_info info; |
31aefaf8 SG |
146 | ulong addr; |
147 | int ret; | |
148 | ||
149 | addr = map_to_sysmem(bflow->buf); | |
150 | info.dev = dev; | |
151 | info.bflow = bflow; | |
79f66351 | 152 | ret = pxe_setup_ctx(&ctx, &cmdtp, extlinux_getfile, &info, true, |
a7e4dffc | 153 | bflow->fname, false); |
31aefaf8 SG |
154 | if (ret) |
155 | return log_msg_ret("ctx", -EINVAL); | |
156 | ||
157 | ret = pxe_process(&ctx, addr, false); | |
158 | if (ret) | |
159 | return log_msg_ret("bread", -EINVAL); | |
160 | ||
161 | return 0; | |
162 | } | |
163 | ||
79f66351 | 164 | static int extlinux_bootmeth_bind(struct udevice *dev) |
31aefaf8 SG |
165 | { |
166 | struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev); | |
167 | ||
168 | plat->desc = IS_ENABLED(CONFIG_BOOTSTD_FULL) ? | |
79f66351 | 169 | "Extlinux boot from a block device" : "extlinux"; |
31aefaf8 SG |
170 | |
171 | return 0; | |
172 | } | |
173 | ||
79f66351 SG |
174 | static struct bootmeth_ops extlinux_bootmeth_ops = { |
175 | .get_state_desc = extlinux_get_state_desc, | |
176 | .check = extlinux_check, | |
177 | .read_bootflow = extlinux_read_bootflow, | |
31aefaf8 | 178 | .read_file = bootmeth_common_read_file, |
79f66351 | 179 | .boot = extlinux_boot, |
31aefaf8 SG |
180 | }; |
181 | ||
79f66351 SG |
182 | static const struct udevice_id extlinux_bootmeth_ids[] = { |
183 | { .compatible = "u-boot,extlinux" }, | |
31aefaf8 SG |
184 | { } |
185 | }; | |
186 | ||
79f66351 SG |
187 | U_BOOT_DRIVER(bootmeth_extlinux) = { |
188 | .name = "bootmeth_extlinux", | |
31aefaf8 | 189 | .id = UCLASS_BOOTMETH, |
79f66351 SG |
190 | .of_match = extlinux_bootmeth_ids, |
191 | .ops = &extlinux_bootmeth_ops, | |
192 | .bind = extlinux_bootmeth_bind, | |
31aefaf8 | 193 | }; |