// SPDX-License-Identifier: GPL-2.0+ /* * Aurora Innovation, Inc. Copyright 2022. * */ #include #include #include #include #include #include #include static bool partition_is_on_device(const struct efi_device_path *device, const struct efi_device_path *part, __u32 *part_no) { size_t d_len, p_len; const struct efi_device_path *p, *d; for (d = device; d->type != DEVICE_PATH_TYPE_END; d = (void *)d + d->length) { } d_len = (void *)d - (void *)device; for (p = part; p->type != DEVICE_PATH_TYPE_END && !(p->type == DEVICE_PATH_TYPE_MEDIA_DEVICE && p->sub_type == DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH); p = (void *)p + p->length) { } if (p->type != DEVICE_PATH_TYPE_MEDIA_DEVICE || p->sub_type != DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH) { // Not a partition. return false; } p_len = (void *)p - (void *)part; if (p_len == d_len && !memcmp(device, part, p_len)) { if (part_no) *part_no = ((__u32 *)p)[1]; return true; } return false; } /** * part_self_find() - Check if a device contains the loaded-image path * * @udev: Block device to check * @loaded_image_path: EFI path of the loaded image * Return 0 if found, -ENOENT if not, other -ve value on error */ static int part_self_find(struct udevice *udev, struct efi_device_path *loaded_image_path) { struct blk_desc *desc = dev_get_uclass_plat(udev); if (desc->uclass_id == UCLASS_EFI_MEDIA) { struct efi_media_plat *plat = dev_get_plat(udev->parent); u32 loader_part_no; if (partition_is_on_device(plat->device_path, loaded_image_path, &loader_part_no)) { char env[256]; int ret; ret = snprintf(env, sizeof(env), "%s %x:%x", blk_get_uclass_name(desc->uclass_id), desc->devnum, loader_part_no); if (ret < 0 || ret == sizeof(env)) return -ENOSPC; if (env_set("target_part", env)) return -ENOMEM; return 0; } } return -ENOENT; } /** * part_blk_find() - Check if a device contains a partition with a type uuid * * @udev: Block device to check * @uuid: UUID to search for (in string form) * Return 0 if found, -ENOENT if not, other -ve value on error */ static int part_blk_find(struct udevice *udev, const char *uuid) { struct blk_desc *desc = dev_get_uclass_plat(udev); int i; for (i = 1; i <= MAX_SEARCH_PARTITIONS; i++) { struct disk_partition info; int ret; ret = part_get_info(desc, i, &info); if (ret) break; if (strcasecmp(uuid, info.type_guid) == 0) { char env[256]; ret = snprintf(env, sizeof(env), "%s %x:%x", blk_get_uclass_name(desc->uclass_id), desc->devnum, i); if (ret < 0 || ret == sizeof(env)) return -ENOSPC; debug("Setting target_part to %s\n", env); if (env_set("target_part", env)) return -ENOMEM; return 0; } } return -ENOENT; } static int part_find(int argc, char *const argv[]) { efi_guid_t efi_devpath_guid = EFI_DEVICE_PATH_PROTOCOL_GUID; struct efi_device_path *loaded_image_path = NULL; bool part_self = false; struct driver *d = ll_entry_start(struct driver, driver); const int n_ents = ll_entry_count(struct driver, driver); struct driver *entry; struct udevice *udev; struct uclass *uc; int ret; if (argc != 2) return CMD_RET_USAGE; if (IS_ENABLED(CONFIG_EFI_CLIENT)) { struct efi_boot_services *boot = efi_get_boot(); struct efi_priv *priv = efi_get_priv(); part_self = !strncmp(argv[1], "self", 6); if (part_self) { ret = boot->handle_protocol(priv->loaded_image->device_handle, &efi_devpath_guid, (void **)&loaded_image_path); if (ret) log_warning("failed to get device path for loaded image (ret=%d)", ret); } } ret = uclass_get(UCLASS_BLK, &uc); if (ret) { puts("Could not get BLK uclass.\n"); return CMD_RET_FAILURE; } for (entry = d; entry < d + n_ents; entry++) { if (entry->id != UCLASS_BLK) continue; uclass_foreach_dev(udev, uc) { if (udev->driver != entry) continue; if (IS_ENABLED(CONFIG_EFI_CLIENT) && part_self) ret = part_self_find(udev, loaded_image_path); else ret = part_blk_find(udev, argv[1]); if (!ret) return 0; if (ret != -ENOENT) break; } } return CMD_RET_FAILURE; } static int do_part_find(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { return part_find(argc, argv); } U_BOOT_CMD( part_find, 2, 0, do_part_find, "Find a partition", "\n" "- Examine the list of known partitions for one that has a type\n" " GUID that matches 'guid', expressed in the standard text format.\n" " If successful, the target_part environment variable will be set\n" " to the corresponding 'interface dev:part'.\n" );