]>
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 | ||
31aefaf8 SG |
11 | #include <bootdev.h> |
12 | #include <bootflow.h> | |
13 | #include <bootmeth.h> | |
14 | #include <bootstd.h> | |
15 | #include <command.h> | |
31aefaf8 | 16 | #include <dm.h> |
79f66351 | 17 | #include <extlinux.h> |
31aefaf8 SG |
18 | #include <fs.h> |
19 | #include <malloc.h> | |
20 | #include <mapmem.h> | |
21 | #include <mmc.h> | |
22 | #include <pxe_utils.h> | |
23 | ||
79f66351 | 24 | static int extlinux_get_state_desc(struct udevice *dev, char *buf, int maxsize) |
988cacae SG |
25 | { |
26 | if (IS_ENABLED(CONFIG_SANDBOX)) { | |
27 | int len; | |
28 | ||
29 | len = snprintf(buf, maxsize, "OK"); | |
30 | ||
31 | return len + 1 < maxsize ? 0 : -ENOSPC; | |
32 | } | |
33 | ||
34 | return 0; | |
35 | } | |
36 | ||
79f66351 SG |
37 | static int extlinux_getfile(struct pxe_context *ctx, const char *file_path, |
38 | char *file_addr, ulong *sizep) | |
31aefaf8 | 39 | { |
79f66351 | 40 | struct extlinux_info *info = ctx->userdata; |
31aefaf8 SG |
41 | ulong addr; |
42 | int ret; | |
43 | ||
44 | addr = simple_strtoul(file_addr, NULL, 16); | |
45 | ||
46 | /* Allow up to 1GB */ | |
47 | *sizep = 1 << 30; | |
48 | ret = bootmeth_read_file(info->dev, info->bflow, file_path, addr, | |
49 | sizep); | |
50 | if (ret) | |
51 | return log_msg_ret("read", ret); | |
52 | ||
53 | return 0; | |
54 | } | |
55 | ||
79f66351 | 56 | static int extlinux_check(struct udevice *dev, struct bootflow_iter *iter) |
31aefaf8 SG |
57 | { |
58 | int ret; | |
59 | ||
60 | /* This only works on block devices */ | |
865328c3 | 61 | ret = bootflow_iter_check_blk(iter); |
31aefaf8 SG |
62 | if (ret) |
63 | return log_msg_ret("blk", ret); | |
64 | ||
65 | return 0; | |
66 | } | |
67 | ||
2175e76a | 68 | /** |
79f66351 | 69 | * extlinux_fill_info() - Decode the extlinux file to find out its info |
2175e76a SG |
70 | * |
71 | * @bflow: Bootflow to process | |
72 | * @return 0 if OK, -ve on error | |
73 | */ | |
79f66351 | 74 | static int extlinux_fill_info(struct bootflow *bflow) |
2175e76a SG |
75 | { |
76 | struct membuff mb; | |
77 | char line[200]; | |
78 | char *data; | |
79 | int len; | |
80 | ||
81 | log_debug("parsing bflow file size %x\n", bflow->size); | |
82 | membuff_init(&mb, bflow->buf, bflow->size); | |
83 | membuff_putraw(&mb, bflow->size, true, &data); | |
e58bafc3 | 84 | while (len = membuff_readline(&mb, line, sizeof(line) - 1, ' ', true), len) { |
2175e76a SG |
85 | char *tok, *p = line; |
86 | ||
87 | tok = strsep(&p, " "); | |
88 | if (p) { | |
89 | if (!strcmp("label", tok)) { | |
90 | bflow->os_name = strdup(p); | |
91 | if (!bflow->os_name) | |
92 | return log_msg_ret("os", -ENOMEM); | |
93 | } | |
94 | } | |
95 | } | |
96 | ||
97 | return 0; | |
98 | } | |
99 | ||
79f66351 | 100 | static int extlinux_read_bootflow(struct udevice *dev, struct bootflow *bflow) |
31aefaf8 SG |
101 | { |
102 | struct blk_desc *desc; | |
103 | const char *const *prefixes; | |
104 | struct udevice *bootstd; | |
105 | const char *prefix; | |
106 | loff_t size; | |
107 | int ret, i; | |
108 | ||
109 | ret = uclass_first_device_err(UCLASS_BOOTSTD, &bootstd); | |
110 | if (ret) | |
111 | return log_msg_ret("std", ret); | |
112 | ||
113 | /* If a block device, we require a partition table */ | |
114 | if (bflow->blk && !bflow->part) | |
115 | return -ENOENT; | |
116 | ||
117 | prefixes = bootstd_get_prefixes(bootstd); | |
118 | i = 0; | |
119 | desc = bflow->blk ? dev_get_uclass_plat(bflow->blk) : NULL; | |
120 | do { | |
121 | prefix = prefixes ? prefixes[i] : NULL; | |
122 | ||
79f66351 | 123 | ret = bootmeth_try_file(bflow, desc, prefix, EXTLINUX_FNAME); |
31aefaf8 SG |
124 | } while (ret && prefixes && prefixes[++i]); |
125 | if (ret) | |
126 | return log_msg_ret("try", ret); | |
127 | size = bflow->size; | |
128 | ||
129 | ret = bootmeth_alloc_file(bflow, 0x10000, 1); | |
130 | if (ret) | |
131 | return log_msg_ret("read", ret); | |
132 | ||
79f66351 | 133 | ret = extlinux_fill_info(bflow); |
2175e76a SG |
134 | if (ret) |
135 | return log_msg_ret("inf", ret); | |
136 | ||
31aefaf8 SG |
137 | return 0; |
138 | } | |
139 | ||
79f66351 | 140 | static int extlinux_boot(struct udevice *dev, struct bootflow *bflow) |
31aefaf8 SG |
141 | { |
142 | struct cmd_tbl cmdtp = {}; /* dummy */ | |
143 | struct pxe_context ctx; | |
79f66351 | 144 | struct extlinux_info info; |
31aefaf8 SG |
145 | ulong addr; |
146 | int ret; | |
147 | ||
148 | addr = map_to_sysmem(bflow->buf); | |
149 | info.dev = dev; | |
150 | info.bflow = bflow; | |
79f66351 | 151 | ret = pxe_setup_ctx(&ctx, &cmdtp, extlinux_getfile, &info, true, |
a7e4dffc | 152 | bflow->fname, false); |
31aefaf8 SG |
153 | if (ret) |
154 | return log_msg_ret("ctx", -EINVAL); | |
155 | ||
156 | ret = pxe_process(&ctx, addr, false); | |
157 | if (ret) | |
158 | return log_msg_ret("bread", -EINVAL); | |
159 | ||
160 | return 0; | |
161 | } | |
162 | ||
79f66351 | 163 | static int extlinux_bootmeth_bind(struct udevice *dev) |
31aefaf8 SG |
164 | { |
165 | struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev); | |
166 | ||
167 | plat->desc = IS_ENABLED(CONFIG_BOOTSTD_FULL) ? | |
79f66351 | 168 | "Extlinux boot from a block device" : "extlinux"; |
31aefaf8 SG |
169 | |
170 | return 0; | |
171 | } | |
172 | ||
79f66351 SG |
173 | static struct bootmeth_ops extlinux_bootmeth_ops = { |
174 | .get_state_desc = extlinux_get_state_desc, | |
175 | .check = extlinux_check, | |
176 | .read_bootflow = extlinux_read_bootflow, | |
31aefaf8 | 177 | .read_file = bootmeth_common_read_file, |
79f66351 | 178 | .boot = extlinux_boot, |
31aefaf8 SG |
179 | }; |
180 | ||
79f66351 SG |
181 | static const struct udevice_id extlinux_bootmeth_ids[] = { |
182 | { .compatible = "u-boot,extlinux" }, | |
31aefaf8 SG |
183 | { } |
184 | }; | |
185 | ||
5986d46f SG |
186 | /* Put an number before 'extlinux' to provide a default ordering */ |
187 | U_BOOT_DRIVER(bootmeth_1extlinux) = { | |
79f66351 | 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 | }; |