// SPDX-License-Identifier: GPL-2.0+ /* * Uclass implementation for standard boot * * Copyright 2021 Google LLC * Written by Simon Glass */ #include #include #include #include #include #include #include #include #include #include #include DECLARE_GLOBAL_DATA_PTR; /* These are used if filename-prefixes is not present */ const char *const default_prefixes[] = {"/", "/boot/", NULL}; static int bootstd_of_to_plat(struct udevice *dev) { struct bootstd_priv *priv = dev_get_priv(dev); int ret; if (IS_ENABLED(CONFIG_BOOTSTD_FULL)) { /* Don't check errors since livetree and flattree are different */ ret = dev_read_string_list(dev, "filename-prefixes", &priv->prefixes); dev_read_string_list(dev, "bootdev-order", &priv->bootdev_order); priv->theme = ofnode_find_subnode(dev_ofnode(dev), "theme"); } return 0; } static void bootstd_clear_glob_(struct bootstd_priv *priv) { struct bootflow *bflow; alist_for_each(bflow, &priv->bootflows) bootflow_remove(bflow); alist_empty(&priv->bootflows); } void bootstd_clear_glob(void) { struct bootstd_priv *std; if (bootstd_get_priv(&std)) return; bootstd_clear_glob_(std); } int bootstd_add_bootflow(struct bootflow *bflow) { struct bootstd_priv *std; int ret; ret = bootstd_get_priv(&std); if (ret) return ret; ret = std->bootflows.count; bflow = alist_add(&std->bootflows, *bflow); if (!bflow) return log_msg_ret("bf2", -ENOMEM); return ret; } int bootstd_clear_bootflows_for_bootdev(struct udevice *dev) { struct bootstd_priv *std = bootstd_try_priv(); struct bootflow *from, *to; /* if bootstd does not exist we cannot have any bootflows */ if (!std) return 0; /* Drop any bootflows that mention this dev */ alist_for_each_filter(from, to, &std->bootflows) { if (from->dev == dev) bootflow_remove(from); else *to++ = *from; } alist_update_end(&std->bootflows, to); return 0; } static int bootstd_remove(struct udevice *dev) { struct bootstd_priv *priv = dev_get_priv(dev); free(priv->prefixes); free(priv->bootdev_order); bootstd_clear_glob_(priv); return 0; } const char *const *const bootstd_get_bootdev_order(struct udevice *dev, bool *okp) { struct bootstd_priv *std = dev_get_priv(dev); const char *targets = env_get("boot_targets"); *okp = true; log_debug("- targets %s %p\n", targets, std->bootdev_order); if (targets && *targets) { str_free_list(std->env_order); std->env_order = str_to_list(targets); if (!std->env_order) { *okp = false; return NULL; } return std->env_order; } return std->bootdev_order; } const char *const *const bootstd_get_prefixes(struct udevice *dev) { struct bootstd_priv *std = dev_get_priv(dev); return std->prefixes ? std->prefixes : default_prefixes; } struct bootstd_priv *bootstd_try_priv(void) { struct udevice *dev; dev = uclass_try_first_device(UCLASS_BOOTSTD); if (!dev || !device_active(dev)) return NULL; return dev_get_priv(dev); } int bootstd_get_priv(struct bootstd_priv **stdp) { struct udevice *dev; int ret; ret = uclass_first_device_err(UCLASS_BOOTSTD, &dev); if (ret) return ret; *stdp = dev_get_priv(dev); return 0; } static int bootstd_probe(struct udevice *dev) { struct bootstd_priv *std = dev_get_priv(dev); alist_init_struct(&std->bootflows, struct bootflow); return 0; } /* For now, bind the bootmethod device if none are found in the devicetree */ int dm_scan_other(bool pre_reloc_only) { struct driver *drv = ll_entry_start(struct driver, driver); const int n_ents = ll_entry_count(struct driver, driver); struct udevice *dev, *bootstd; int i, ret; /* These are not needed before relocation */ if (!(gd->flags & GD_FLG_RELOC)) return 0; /* Create a bootstd device if needed */ uclass_find_first_device(UCLASS_BOOTSTD, &bootstd); if (!bootstd) { ret = device_bind_driver(gd->dm_root, "bootstd_drv", "bootstd", &bootstd); if (ret) return log_msg_ret("bootstd", ret); } /* If there are no bootmeth devices, create them */ uclass_find_first_device(UCLASS_BOOTMETH, &dev); if (dev) return 0; for (i = 0; i < n_ents; i++, drv++) { if (drv->id == UCLASS_BOOTMETH) { const char *name = drv->name; if (!strncmp("bootmeth_", name, 9)) name += 9; ret = device_bind(bootstd, drv, name, 0, ofnode_null(), &dev); if (ret) return log_msg_ret("meth", ret); } } return 0; } static const struct udevice_id bootstd_ids[] = { { .compatible = "u-boot,boot-std" }, { } }; U_BOOT_DRIVER(bootstd_drv) = { .id = UCLASS_BOOTSTD, .name = "bootstd_drv", .of_to_plat = bootstd_of_to_plat, .probe = bootstd_probe, .remove = bootstd_remove, .of_match = bootstd_ids, .priv_auto = sizeof(struct bootstd_priv), }; UCLASS_DRIVER(bootstd) = { .id = UCLASS_BOOTSTD, .name = "bootstd", #if CONFIG_IS_ENABLED(OF_REAL) .post_bind = dm_scan_fdt_dev, #endif };