"Das U-Boot" Source Tree
at jcs/rk3128 272 lines 6.5 kB view raw
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Bootmethod for booting via a U-Boot script 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 <blk.h> 12#include <bootflow.h> 13#include <bootmeth.h> 14#include <bootstd.h> 15#include <dm.h> 16#include <env.h> 17#include <fs.h> 18#include <image.h> 19#include <malloc.h> 20#include <mapmem.h> 21#include <net.h> 22 23#define SCRIPT_FNAME1 "boot.scr.uimg" 24#define SCRIPT_FNAME2 "boot.scr" 25 26static int script_check(struct udevice *dev, struct bootflow_iter *iter) 27{ 28 /* This works on block devices, network devices and SPI Flash */ 29 if (iter->method_flags & BOOTFLOW_METHF_PXE_ONLY) 30 return log_msg_ret("pxe", -ENOTSUPP); 31 32 return 0; 33} 34 35/** 36 * script_fill_info() - Decode the U-Boot script to find out distro info 37 * 38 * @bflow: Bootflow to process 39 * @return 0 if OK, -ve on error 40 */ 41static int script_fill_info(struct bootflow *bflow) 42{ 43 char *name = NULL; 44 char *data; 45 uint len; 46 int ret; 47 48 log_debug("parsing bflow file size %x\n", bflow->size); 49 50 ret = image_locate_script(bflow->buf, bflow->size, NULL, NULL, &data, &len); 51 if (!ret) { 52 if (strstr(data, "armbianEnv")) 53 name = "Armbian"; 54 } 55 56 if (name) { 57 bflow->os_name = strdup(name); 58 if (!bflow->os_name) 59 return log_msg_ret("os", -ENOMEM); 60 } 61 62 return 0; 63} 64 65static int script_read_bootflow_file(struct udevice *bootstd, 66 struct bootflow *bflow) 67{ 68 struct blk_desc *desc = NULL; 69 const char *const *prefixes; 70 const char *prefix; 71 int ret, i; 72 73 ret = uclass_first_device_err(UCLASS_BOOTSTD, &bootstd); 74 if (ret) 75 return log_msg_ret("std", ret); 76 77 if (bflow->blk) { 78 /* We require a partition table */ 79 if (!bflow->part) 80 return -ENOENT; 81 desc = dev_get_uclass_plat(bflow->blk); 82 } 83 84 prefixes = bootstd_get_prefixes(bootstd); 85 i = 0; 86 do { 87 prefix = prefixes ? prefixes[i] : NULL; 88 89 ret = bootmeth_try_file(bflow, desc, prefix, SCRIPT_FNAME1); 90 if (ret) 91 ret = bootmeth_try_file(bflow, desc, prefix, 92 SCRIPT_FNAME2); 93 } while (ret && prefixes && prefixes[++i]); 94 if (ret) 95 return log_msg_ret("try", ret); 96 97 bflow->subdir = strdup(prefix ? prefix : ""); 98 if (!bflow->subdir) 99 return log_msg_ret("prefix", -ENOMEM); 100 101 ret = bootmeth_alloc_file(bflow, 0x10000, ARCH_DMA_MINALIGN, 102 (enum bootflow_img_t)IH_TYPE_SCRIPT); 103 if (ret) 104 return log_msg_ret("read", ret); 105 106 ret = script_fill_info(bflow); 107 if (ret) 108 return log_msg_ret("inf", ret); 109 110 ret = bootmeth_alloc_other(bflow, "boot.bmp", BFI_LOGO, 111 &bflow->logo, &bflow->logo_size); 112 /* ignore error */ 113 114 return 0; 115} 116 117static int script_read_bootflow_net(struct bootflow *bflow) 118{ 119 const char *addr_str; 120 int size, ret; 121 char *fname; 122 ulong addr; 123 124 /* figure out the load address */ 125 addr_str = env_get("scriptaddr"); 126 addr = addr_str ? hextoul(addr_str, NULL) : image_load_addr; 127 128 fname = env_get("boot_script_dhcp"); 129 if (!fname) 130 return log_msg_ret("dhc", -EINVAL); 131 132 ret = dhcp_run(addr, fname, true); 133 if (ret) 134 return log_msg_ret("dhc", ret); 135 136 size = env_get_hex("filesize", 0); 137 if (!size) 138 return log_msg_ret("sz", -EINVAL); 139 140 bflow->buf = malloc(size + 1); 141 if (!bflow->buf) 142 return log_msg_ret("buf", -ENOMEM); 143 memcpy(bflow->buf, map_sysmem(addr, size), size); 144 bflow->buf[size] = '\0'; 145 bflow->size = size; 146 bflow->state = BOOTFLOWST_READY; 147 148 return 0; 149} 150 151static int script_read_bootflow(struct udevice *dev, struct bootflow *bflow) 152{ 153 const struct udevice *media = dev_get_parent(bflow->dev); 154 struct udevice *bootstd; 155 int ret; 156 157 ret = uclass_first_device_err(UCLASS_BOOTSTD, &bootstd); 158 if (ret) 159 return log_msg_ret("std", ret); 160 161 if (IS_ENABLED(CONFIG_CMD_DHCP) && 162 device_get_uclass_id(media) == UCLASS_ETH) { 163 /* we only support reading from one device, so ignore 'dev' */ 164 ret = script_read_bootflow_net(bflow); 165 if (ret) 166 return log_msg_ret("net", ret); 167 } else { 168 ret = script_read_bootflow_file(bootstd, bflow); 169 if (ret) 170 return log_msg_ret("blk", ret); 171 } 172 173 return 0; 174} 175 176static int script_set_bootflow(struct udevice *dev, struct bootflow *bflow, 177 char *buf, int size) 178{ 179 buf[size] = '\0'; 180 bflow->buf = buf; 181 bflow->size = size; 182 bflow->state = BOOTFLOWST_READY; 183 184 return 0; 185} 186 187static int script_boot(struct udevice *dev, struct bootflow *bflow) 188{ 189 struct blk_desc *desc; 190 ulong addr; 191 int ret = 0; 192 193 if (bflow->blk) { 194 desc = dev_get_uclass_plat(bflow->blk); 195 if (desc->uclass_id == UCLASS_USB) { 196 ret = env_set("devtype", "usb"); 197 } else { 198 /* 199 * If the uclass is AHCI, but the driver is ATA 200 * (not scsi), set devtype to sata 201 */ 202 if (IS_ENABLED(CONFIG_SATA) && 203 desc->uclass_id == UCLASS_AHCI) 204 ret = env_set("devtype", "sata"); 205 else 206 ret = env_set("devtype", blk_get_devtype(bflow->blk)); 207 } 208 if (!ret) 209 ret = env_set_hex("devnum", desc->devnum); 210 if (!ret) 211 ret = env_set_hex("distro_bootpart", bflow->part); 212 if (!ret) 213 ret = env_set("prefix", bflow->subdir); 214 if (!ret && IS_ENABLED(CONFIG_ARCH_SUNXI) && 215 !strcmp("mmc", blk_get_devtype(bflow->blk))) 216 ret = env_set_hex("mmc_bootdev", desc->devnum); 217 } else { 218 const struct udevice *media = dev_get_parent(bflow->dev); 219 220 ret = env_set("devtype", 221 uclass_get_name(device_get_uclass_id(media))); 222 if (!ret) 223 ret = env_set_hex("devnum", dev_seq(media)); 224 } 225 if (ret) 226 return log_msg_ret("env", ret); 227 228 log_debug("devtype: %s\n", env_get("devtype")); 229 log_debug("devnum: %s\n", env_get("devnum")); 230 log_debug("distro_bootpart: %s\n", env_get("distro_bootpart")); 231 log_debug("prefix: %s\n", env_get("prefix")); 232 log_debug("mmc_bootdev: %s\n", env_get("mmc_bootdev")); 233 234 addr = map_to_sysmem(bflow->buf); 235 ret = cmd_source_script(addr, NULL, NULL); 236 if (ret) 237 return log_msg_ret("boot", ret); 238 239 return 0; 240} 241 242static int script_bootmeth_bind(struct udevice *dev) 243{ 244 struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev); 245 246 plat->desc = IS_ENABLED(CONFIG_BOOTSTD_FULL) ? 247 "Script boot from a block device" : "script"; 248 249 return 0; 250} 251 252static struct bootmeth_ops script_bootmeth_ops = { 253 .check = script_check, 254 .read_bootflow = script_read_bootflow, 255 .set_bootflow = script_set_bootflow, 256 .read_file = bootmeth_common_read_file, 257 .boot = script_boot, 258}; 259 260static const struct udevice_id script_bootmeth_ids[] = { 261 { .compatible = "u-boot,script" }, 262 { } 263}; 264 265/* Put a number before 'script' to provide a default ordering */ 266U_BOOT_DRIVER(bootmeth_2script) = { 267 .name = "bootmeth_script", 268 .id = UCLASS_BOOTMETH, 269 .of_match = script_bootmeth_ids, 270 .ops = &script_bootmeth_ops, 271 .bind = script_bootmeth_bind, 272};