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 <common.h> 12#include <blk.h> 13#include <bootflow.h> 14#include <bootmeth.h> 15#include <bootstd.h> 16#include <dm.h> 17#include <env.h> 18#include <fs.h> 19#include <image.h> 20#include <malloc.h> 21#include <mapmem.h> 22#include <net.h> 23 24#define SCRIPT_FNAME1 "boot.scr.uimg" 25#define SCRIPT_FNAME2 "boot.scr" 26 27static int script_check(struct udevice *dev, struct bootflow_iter *iter) 28{ 29 /* This works on block devices, network devices and SPI Flash */ 30 if (iter->method_flags & BOOTFLOW_METHF_PXE_ONLY) 31 return log_msg_ret("pxe", -ENOTSUPP); 32 33 return 0; 34} 35 36/** 37 * script_fill_info() - Decode the U-Boot script to find out distro info 38 * 39 * @bflow: Bootflow to process 40 * @return 0 if OK, -ve on error 41 */ 42static int script_fill_info(struct bootflow *bflow) 43{ 44 char *name = NULL; 45 char *data; 46 uint len; 47 int ret; 48 49 log_debug("parsing bflow file size %x\n", bflow->size); 50 51 ret = image_locate_script(bflow->buf, bflow->size, NULL, NULL, &data, &len); 52 if (!ret) { 53 if (strstr(data, "armbianEnv")) 54 name = "Armbian"; 55 } 56 57 if (name) { 58 bflow->os_name = strdup(name); 59 if (!bflow->os_name) 60 return log_msg_ret("os", -ENOMEM); 61 } 62 63 return 0; 64} 65 66static int script_read_bootflow_file(struct udevice *bootstd, 67 struct bootflow *bflow) 68{ 69 struct blk_desc *desc = NULL; 70 const char *const *prefixes; 71 const char *prefix; 72 int ret, i; 73 74 ret = uclass_first_device_err(UCLASS_BOOTSTD, &bootstd); 75 if (ret) 76 return log_msg_ret("std", ret); 77 78 if (bflow->blk) { 79 /* We require a partition table */ 80 if (!bflow->part) 81 return -ENOENT; 82 desc = dev_get_uclass_plat(bflow->blk); 83 } 84 85 prefixes = bootstd_get_prefixes(bootstd); 86 i = 0; 87 do { 88 prefix = prefixes ? prefixes[i] : NULL; 89 90 ret = bootmeth_try_file(bflow, desc, prefix, SCRIPT_FNAME1); 91 if (ret) 92 ret = bootmeth_try_file(bflow, desc, prefix, 93 SCRIPT_FNAME2); 94 } while (ret && prefixes && prefixes[++i]); 95 if (ret) 96 return log_msg_ret("try", ret); 97 98 bflow->subdir = strdup(prefix ? prefix : ""); 99 if (!bflow->subdir) 100 return log_msg_ret("prefix", -ENOMEM); 101 102 ret = bootmeth_alloc_file(bflow, 0x10000, ARCH_DMA_MINALIGN); 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", &bflow->logo, 111 &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 = dev_get_uclass_plat(bflow->blk); 190 ulong addr; 191 int ret = 0; 192 193 if (desc->uclass_id == UCLASS_USB) { 194 ret = env_set("devtype", "usb"); 195 } else { 196 /* If the uclass is AHCI, but the driver is ATA 197 * (not scsi), set devtype to sata 198 */ 199 if (IS_ENABLED(CONFIG_SATA) && 200 desc->uclass_id == UCLASS_AHCI) 201 ret = env_set("devtype", "sata"); 202 else 203 ret = env_set("devtype", blk_get_devtype(bflow->blk)); 204 } 205 if (!ret) 206 ret = env_set_hex("devnum", desc->devnum); 207 if (!ret) 208 ret = env_set_hex("distro_bootpart", bflow->part); 209 if (!ret) 210 ret = env_set("prefix", bflow->subdir); 211 if (!ret && IS_ENABLED(CONFIG_ARCH_SUNXI) && 212 !strcmp("mmc", blk_get_devtype(bflow->blk))) 213 ret = env_set_hex("mmc_bootdev", desc->devnum); 214 if (ret) 215 return log_msg_ret("env", ret); 216 217 log_debug("devtype: %s\n", env_get("devtype")); 218 log_debug("devnum: %s\n", env_get("devnum")); 219 log_debug("distro_bootpart: %s\n", env_get("distro_bootpart")); 220 log_debug("prefix: %s\n", env_get("prefix")); 221 log_debug("mmc_bootdev: %s\n", env_get("mmc_bootdev")); 222 223 addr = map_to_sysmem(bflow->buf); 224 ret = cmd_source_script(addr, NULL, NULL); 225 if (ret) 226 return log_msg_ret("boot", ret); 227 228 return 0; 229} 230 231static int script_bootmeth_bind(struct udevice *dev) 232{ 233 struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev); 234 235 plat->desc = IS_ENABLED(CONFIG_BOOTSTD_FULL) ? 236 "Script boot from a block device" : "script"; 237 238 return 0; 239} 240 241static struct bootmeth_ops script_bootmeth_ops = { 242 .check = script_check, 243 .read_bootflow = script_read_bootflow, 244 .set_bootflow = script_set_bootflow, 245 .read_file = bootmeth_common_read_file, 246 .boot = script_boot, 247}; 248 249static const struct udevice_id script_bootmeth_ids[] = { 250 { .compatible = "u-boot,script" }, 251 { } 252}; 253 254/* Put an number before 'script' to provide a default ordering */ 255U_BOOT_DRIVER(bootmeth_2script) = { 256 .name = "bootmeth_script", 257 .id = UCLASS_BOOTMETH, 258 .of_match = script_bootmeth_ids, 259 .ops = &script_bootmeth_ops, 260 .bind = script_bootmeth_bind, 261}; 262