"Das U-Boot" Source Tree
at master 471 lines 11 kB view raw
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright 2021 Google LLC 4 * Written by Simon Glass <sjg@chromium.org> 5 */ 6 7#define LOG_CATEGORY UCLASS_BOOTSTD 8 9#include <alist.h> 10#include <blk.h> 11#include <bootflow.h> 12#include <bootmeth.h> 13#include <bootstd.h> 14#include <dm.h> 15#include <env_internal.h> 16#include <fs.h> 17#include <malloc.h> 18#include <mapmem.h> 19#include <dm/uclass-internal.h> 20 21DECLARE_GLOBAL_DATA_PTR; 22 23int bootmeth_get_state_desc(struct udevice *dev, char *buf, int maxsize) 24{ 25 const struct bootmeth_ops *ops = bootmeth_get_ops(dev); 26 27 if (!ops->get_state_desc) 28 return -ENOSYS; 29 30 return ops->get_state_desc(dev, buf, maxsize); 31} 32 33int bootmeth_check(struct udevice *dev, struct bootflow_iter *iter) 34{ 35 const struct bootmeth_ops *ops = bootmeth_get_ops(dev); 36 37 if (!ops->check) 38 return 0; 39 40 return ops->check(dev, iter); 41} 42 43int bootmeth_read_bootflow(struct udevice *dev, struct bootflow *bflow) 44{ 45 const struct bootmeth_ops *ops = bootmeth_get_ops(dev); 46 47 if (!ops->read_bootflow) 48 return -ENOSYS; 49 50 return ops->read_bootflow(dev, bflow); 51} 52 53int bootmeth_set_bootflow(struct udevice *dev, struct bootflow *bflow, 54 char *buf, int size) 55{ 56 const struct bootmeth_ops *ops = bootmeth_get_ops(dev); 57 58 if (!ops->set_bootflow) 59 return -ENOSYS; 60 61 return ops->set_bootflow(dev, bflow, buf, size); 62} 63 64#if CONFIG_IS_ENABLED(BOOTSTD_FULL) 65int bootmeth_read_all(struct udevice *dev, struct bootflow *bflow) 66{ 67 const struct bootmeth_ops *ops = bootmeth_get_ops(dev); 68 69 if (!ops->read_all) 70 return -ENOSYS; 71 72 return ops->read_all(dev, bflow); 73} 74#endif /* BOOTSTD_FULL */ 75 76int bootmeth_boot(struct udevice *dev, struct bootflow *bflow) 77{ 78 const struct bootmeth_ops *ops = bootmeth_get_ops(dev); 79 80 if (!ops->boot) 81 return -ENOSYS; 82 83 return ops->boot(dev, bflow); 84} 85 86int bootmeth_read_file(struct udevice *dev, struct bootflow *bflow, 87 const char *file_path, ulong addr, 88 enum bootflow_img_t type, ulong *sizep) 89{ 90 const struct bootmeth_ops *ops = bootmeth_get_ops(dev); 91 92 if (!ops->read_file) 93 return -ENOSYS; 94 95 return ops->read_file(dev, bflow, file_path, addr, type, sizep); 96} 97 98int bootmeth_get_bootflow(struct udevice *dev, struct bootflow *bflow) 99{ 100 const struct bootmeth_ops *ops = bootmeth_get_ops(dev); 101 102 if (!ops->read_bootflow) 103 return -ENOSYS; 104 bootflow_init(bflow, NULL, dev); 105 106 return ops->read_bootflow(dev, bflow); 107} 108 109int bootmeth_setup_iter_order(struct bootflow_iter *iter, bool include_global) 110{ 111 struct bootstd_priv *std; 112 struct udevice **order; 113 int count; 114 int ret; 115 116 ret = bootstd_get_priv(&std); 117 if (ret) 118 return ret; 119 120 /* Create an array large enough */ 121 count = std->bootmeth_count ? std->bootmeth_count : 122 uclass_id_count(UCLASS_BOOTMETH); 123 if (!count) 124 return log_msg_ret("count", -ENOENT); 125 126 order = calloc(count, sizeof(struct udevice *)); 127 if (!order) 128 return log_msg_ret("order", -ENOMEM); 129 130 /* If we have an ordering, copy it */ 131 if (IS_ENABLED(CONFIG_BOOTSTD_FULL) && std->bootmeth_count) { 132 int i; 133 134 /* 135 * We don't support skipping global bootmeths. Instead, the user 136 * should omit them from the ordering 137 */ 138 if (!include_global) 139 return log_msg_ret("glob", -EPERM); 140 memcpy(order, std->bootmeth_order, 141 count * sizeof(struct bootmeth *)); 142 143 if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL)) { 144 for (i = 0; i < count; i++) { 145 struct udevice *dev = order[i]; 146 struct bootmeth_uc_plat *ucp; 147 bool is_global; 148 149 ucp = dev_get_uclass_plat(dev); 150 is_global = ucp->flags & 151 BOOTMETHF_GLOBAL; 152 if (is_global) { 153 iter->first_glob_method = i; 154 break; 155 } 156 } 157 } 158 } else { 159 struct udevice *dev; 160 int i, upto, pass; 161 162 /* 163 * Do two passes, one to find the normal bootmeths and another 164 * to find the global ones, if required, The global ones go at 165 * the end. 166 */ 167 for (pass = 0, upto = 0; pass < 1 + include_global; pass++) { 168 if (pass) 169 iter->first_glob_method = upto; 170 /* 171 * Get a list of bootmethods, in seq order (i.e. using 172 * aliases). There may be gaps so try to count up high 173 * enough to find them all. 174 */ 175 for (i = 0; upto < count && i < 20 + count * 2; i++) { 176 struct bootmeth_uc_plat *ucp; 177 bool is_global; 178 179 ret = uclass_get_device_by_seq(UCLASS_BOOTMETH, 180 i, &dev); 181 if (ret) 182 continue; 183 ucp = dev_get_uclass_plat(dev); 184 is_global = 185 IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && 186 (ucp->flags & BOOTMETHF_GLOBAL); 187 if (pass ? is_global : !is_global) 188 order[upto++] = dev; 189 } 190 } 191 count = upto; 192 } 193 if (!count) 194 return log_msg_ret("count2", -ENOENT); 195 196 if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && include_global && 197 iter->first_glob_method != -1 && iter->first_glob_method != count) { 198 iter->cur_method = iter->first_glob_method; 199 iter->doing_global = true; 200 } 201 iter->method_order = order; 202 iter->num_methods = count; 203 204 return 0; 205} 206 207int bootmeth_set_order(const char *order_str) 208{ 209 struct bootstd_priv *std; 210 struct udevice **order; 211 int count, ret, i, len; 212 const char *s, *p; 213 214 ret = bootstd_get_priv(&std); 215 if (ret) 216 return ret; 217 218 if (!order_str) { 219 free(std->bootmeth_order); 220 std->bootmeth_order = NULL; 221 std->bootmeth_count = 0; 222 return 0; 223 } 224 225 /* Create an array large enough */ 226 count = uclass_id_count(UCLASS_BOOTMETH); 227 if (!count) 228 return log_msg_ret("count", -ENOENT); 229 230 order = calloc(count + 1, sizeof(struct udevice *)); 231 if (!order) 232 return log_msg_ret("order", -ENOMEM); 233 234 for (i = 0, s = order_str; *s && i < count; s = p + (*p == ' '), i++) { 235 struct udevice *dev; 236 237 p = strchrnul(s, ' '); 238 len = p - s; 239 ret = uclass_find_device_by_namelen(UCLASS_BOOTMETH, s, len, 240 &dev); 241 if (ret) { 242 printf("Unknown bootmeth '%.*s'\n", len, s); 243 free(order); 244 return ret; 245 } 246 order[i] = dev; 247 } 248 order[i] = NULL; 249 free(std->bootmeth_order); 250 std->bootmeth_order = order; 251 std->bootmeth_count = i; 252 253 return 0; 254} 255 256int bootmeth_set_property(const char *name, const char *property, const char *value) 257{ 258 int ret; 259 int len; 260 struct udevice *dev; 261 const struct bootmeth_ops *ops; 262 263 len = strlen(name); 264 265 ret = uclass_find_device_by_namelen(UCLASS_BOOTMETH, name, len, 266 &dev); 267 if (ret) { 268 printf("Unknown bootmeth '%s'\n", name); 269 return ret; 270 } 271 272 ops = bootmeth_get_ops(dev); 273 if (!ops->set_property) { 274 printf("set_property not found\n"); 275 return -ENODEV; 276 } 277 278 return ops->set_property(dev, property, value); 279} 280 281int bootmeth_setup_fs(struct bootflow *bflow, struct blk_desc *desc) 282{ 283 int ret; 284 285 if (desc) { 286 ret = fs_set_blk_dev_with_part(desc, bflow->part); 287 if (ret) 288 return log_msg_ret("set", ret); 289 } else if (IS_ENABLED(CONFIG_BOOTSTD_FULL) && bflow->fs_type) { 290 fs_set_type(bflow->fs_type); 291 } 292 293 return 0; 294} 295 296int bootmeth_try_file(struct bootflow *bflow, struct blk_desc *desc, 297 const char *prefix, const char *fname) 298{ 299 char path[200]; 300 loff_t size; 301 int ret, ret2; 302 303 snprintf(path, sizeof(path), "%s%s", prefix ? prefix : "", fname); 304 log_debug("trying: %s\n", path); 305 306 free(bflow->fname); 307 bflow->fname = strdup(path); 308 if (!bflow->fname) 309 return log_msg_ret("name", -ENOMEM); 310 311 if (IS_ENABLED(CONFIG_BOOTSTD_FULL) && bflow->fs_type) 312 fs_set_type(bflow->fs_type); 313 314 ret = fs_size(path, &size); 315 log_debug(" %s - err=%d\n", path, ret); 316 317 /* Sadly FS closes the file after fs_size() so we must redo this */ 318 ret2 = bootmeth_setup_fs(bflow, desc); 319 if (ret2) 320 return log_msg_ret("fs", ret2); 321 322 if (ret) 323 return log_msg_ret("size", ret); 324 325 bflow->size = size; 326 bflow->state = BOOTFLOWST_FILE; 327 328 return 0; 329} 330 331int bootmeth_alloc_file(struct bootflow *bflow, uint size_limit, uint align, 332 enum bootflow_img_t type) 333{ 334 struct blk_desc *desc = NULL; 335 void *buf; 336 uint size; 337 int ret; 338 339 size = bflow->size; 340 log_debug(" - script file size %x\n", size); 341 if (size > size_limit) 342 return log_msg_ret("chk", -E2BIG); 343 344 ret = fs_read_alloc(bflow->fname, bflow->size, align, &buf); 345 if (ret) 346 return log_msg_ret("all", ret); 347 348 bflow->state = BOOTFLOWST_READY; 349 bflow->buf = buf; 350 351 if (bflow->blk) 352 desc = dev_get_uclass_plat(bflow->blk); 353 354 if (!bootflow_img_add(bflow, bflow->fname, type, map_to_sysmem(buf), 355 size)) 356 return log_msg_ret("bai", -ENOMEM); 357 358 return 0; 359} 360 361int bootmeth_alloc_other(struct bootflow *bflow, const char *fname, 362 enum bootflow_img_t type, void **bufp, uint *sizep) 363{ 364 struct blk_desc *desc = NULL; 365 char path[200]; 366 loff_t size; 367 void *buf; 368 int ret; 369 370 snprintf(path, sizeof(path), "%s%s", bflow->subdir, fname); 371 log_debug("trying: %s\n", path); 372 373 if (bflow->blk) 374 desc = dev_get_uclass_plat(bflow->blk); 375 376 ret = bootmeth_setup_fs(bflow, desc); 377 if (ret) 378 return log_msg_ret("fs", ret); 379 380 ret = fs_size(path, &size); 381 log_debug(" %s - err=%d\n", path, ret); 382 383 ret = bootmeth_setup_fs(bflow, desc); 384 if (ret) 385 return log_msg_ret("fs", ret); 386 387 ret = fs_read_alloc(path, size, 0, &buf); 388 if (ret) 389 return log_msg_ret("all", ret); 390 391 if (!bootflow_img_add(bflow, bflow->fname, type, map_to_sysmem(buf), 392 size)) 393 return log_msg_ret("boi", -ENOMEM); 394 395 *bufp = buf; 396 *sizep = size; 397 398 return 0; 399} 400 401int bootmeth_common_read_file(struct udevice *dev, struct bootflow *bflow, 402 const char *file_path, ulong addr, 403 enum bootflow_img_t type, ulong *sizep) 404{ 405 struct blk_desc *desc = NULL; 406 loff_t len_read; 407 loff_t size; 408 int ret; 409 410 if (bflow->blk) 411 desc = dev_get_uclass_plat(bflow->blk); 412 413 ret = bootmeth_setup_fs(bflow, desc); 414 if (ret) 415 return log_msg_ret("fs", ret); 416 417 ret = fs_size(file_path, &size); 418 if (ret) 419 return log_msg_ret("size", ret); 420 if (size > *sizep) 421 return log_msg_ret("spc", -ENOSPC); 422 423 ret = bootmeth_setup_fs(bflow, desc); 424 if (ret) 425 return log_msg_ret("fs", ret); 426 427 ret = fs_read(file_path, addr, 0, 0, &len_read); 428 if (ret) 429 return ret; 430 *sizep = len_read; 431 432 if (!bootflow_img_add(bflow, bflow->fname, type, addr, size)) 433 return log_msg_ret("bci", -ENOMEM); 434 435 return 0; 436} 437 438#ifdef CONFIG_BOOTSTD_FULL 439/** 440 * on_bootmeths() - Update the bootmeth order 441 * 442 * This will check for a valid list of bootmeths and only apply it if valid. 443 */ 444static int on_bootmeths(const char *name, const char *value, enum env_op op, 445 int flags) 446{ 447 int ret; 448 449 switch (op) { 450 case env_op_create: 451 case env_op_overwrite: 452 ret = bootmeth_set_order(value); 453 if (ret) 454 return 1; 455 return 0; 456 case env_op_delete: 457 bootmeth_set_order(NULL); 458 fallthrough; 459 default: 460 return 0; 461 } 462} 463U_BOOT_ENV_CALLBACK(bootmeths, on_bootmeths); 464#endif /* CONFIG_BOOTSTD_FULL */ 465 466UCLASS_DRIVER(bootmeth) = { 467 .id = UCLASS_BOOTMETH, 468 .name = "bootmeth", 469 .flags = DM_UC_FLAG_SEQ_ALIAS, 470 .per_device_plat_auto = sizeof(struct bootmeth_uc_plat), 471};