]>
Commit | Line | Data |
---|---|---|
31aefaf8 SG |
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 <[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> | |
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 disto_getfile(struct pxe_context *ctx, const char *file_path, | |
26 | char *file_addr, ulong *sizep) | |
27 | { | |
28 | struct distro_info *info = ctx->userdata; | |
29 | ulong addr; | |
30 | int ret; | |
31 | ||
32 | addr = simple_strtoul(file_addr, NULL, 16); | |
33 | ||
34 | /* Allow up to 1GB */ | |
35 | *sizep = 1 << 30; | |
36 | ret = bootmeth_read_file(info->dev, info->bflow, file_path, addr, | |
37 | sizep); | |
38 | if (ret) | |
39 | return log_msg_ret("read", ret); | |
40 | ||
41 | return 0; | |
42 | } | |
43 | ||
44 | static int distro_check(struct udevice *dev, struct bootflow_iter *iter) | |
45 | { | |
46 | int ret; | |
47 | ||
48 | /* This only works on block devices */ | |
49 | ret = bootflow_iter_uses_blk_dev(iter); | |
50 | if (ret) | |
51 | return log_msg_ret("blk", ret); | |
52 | ||
53 | return 0; | |
54 | } | |
55 | ||
56 | static int distro_read_bootflow(struct udevice *dev, struct bootflow *bflow) | |
57 | { | |
58 | struct blk_desc *desc; | |
59 | const char *const *prefixes; | |
60 | struct udevice *bootstd; | |
61 | const char *prefix; | |
62 | loff_t size; | |
63 | int ret, i; | |
64 | ||
65 | ret = uclass_first_device_err(UCLASS_BOOTSTD, &bootstd); | |
66 | if (ret) | |
67 | return log_msg_ret("std", ret); | |
68 | ||
69 | /* If a block device, we require a partition table */ | |
70 | if (bflow->blk && !bflow->part) | |
71 | return -ENOENT; | |
72 | ||
73 | prefixes = bootstd_get_prefixes(bootstd); | |
74 | i = 0; | |
75 | desc = bflow->blk ? dev_get_uclass_plat(bflow->blk) : NULL; | |
76 | do { | |
77 | prefix = prefixes ? prefixes[i] : NULL; | |
78 | ||
79 | ret = bootmeth_try_file(bflow, desc, prefix, DISTRO_FNAME); | |
80 | } while (ret && prefixes && prefixes[++i]); | |
81 | if (ret) | |
82 | return log_msg_ret("try", ret); | |
83 | size = bflow->size; | |
84 | ||
85 | ret = bootmeth_alloc_file(bflow, 0x10000, 1); | |
86 | if (ret) | |
87 | return log_msg_ret("read", ret); | |
88 | ||
89 | return 0; | |
90 | } | |
91 | ||
92 | static int distro_boot(struct udevice *dev, struct bootflow *bflow) | |
93 | { | |
94 | struct cmd_tbl cmdtp = {}; /* dummy */ | |
95 | struct pxe_context ctx; | |
96 | struct distro_info info; | |
97 | ulong addr; | |
98 | int ret; | |
99 | ||
100 | addr = map_to_sysmem(bflow->buf); | |
101 | info.dev = dev; | |
102 | info.bflow = bflow; | |
103 | ret = pxe_setup_ctx(&ctx, &cmdtp, disto_getfile, &info, true, | |
104 | bflow->subdir); | |
105 | if (ret) | |
106 | return log_msg_ret("ctx", -EINVAL); | |
107 | ||
108 | ret = pxe_process(&ctx, addr, false); | |
109 | if (ret) | |
110 | return log_msg_ret("bread", -EINVAL); | |
111 | ||
112 | return 0; | |
113 | } | |
114 | ||
115 | static int distro_bootmeth_bind(struct udevice *dev) | |
116 | { | |
117 | struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev); | |
118 | ||
119 | plat->desc = IS_ENABLED(CONFIG_BOOTSTD_FULL) ? | |
120 | "Syslinux boot from a block device" : "syslinux"; | |
121 | ||
122 | return 0; | |
123 | } | |
124 | ||
125 | static struct bootmeth_ops distro_bootmeth_ops = { | |
126 | .check = distro_check, | |
127 | .read_bootflow = distro_read_bootflow, | |
128 | .read_file = bootmeth_common_read_file, | |
129 | .boot = distro_boot, | |
130 | }; | |
131 | ||
132 | static const struct udevice_id distro_bootmeth_ids[] = { | |
133 | { .compatible = "u-boot,distro-syslinux" }, | |
134 | { } | |
135 | }; | |
136 | ||
137 | U_BOOT_DRIVER(bootmeth_distro) = { | |
138 | .name = "bootmeth_distro", | |
139 | .id = UCLASS_BOOTMETH, | |
140 | .of_match = distro_bootmeth_ids, | |
141 | .ops = &distro_bootmeth_ops, | |
142 | .bind = distro_bootmeth_bind, | |
143 | }; |