[...] > >> > >> To support multi-image RAW capsules, we would need to: > >> > >> 1. Enhance mkeficapsule to create multi-image capsules? > > There's an equivalent tool in EDKII that can produce a capsule with > > multiple payloads. There were also patches posted for mkeficapsule, > > but need some minor tweaks to merge them > Can you please point me those patches? I did a search myself, but > couldnt find those patches.
https://lore.kernel.org/u-boot/[email protected]/ > > > >> 2. Enhance efi_firmware_raw_set_image() to parse FMP capsule headers and > >> extract multiple binaries? > > That's already supported > Ohokay, will check the codes. This is tested as well, when generating a capsule with multiple payloads from EDKII GenerateCapsule tool > > > >>>> In contrast, the FIT-based capsule approach uses a single capsule file > >>>> to update multiple firmware binaries simultaneously, with the FIT image > >>>> serving as a container that bundles all firmware components together. > >>>> > >>>> 2. The FIT approach (introduced in this series) eliminates hardcoded > >>>> partition names: the current Qualcomm RAW capsule update code (in > >>>> capsule_update.c) hardcodes partition names like "uefi" in the source > >>>> and searches for matching partitions on the device, whereas the > >>>> FIT-based method stores partition names as FIT node names within the > >>>> capsule itself, allowing the same U-Boot binary to work with different > >>>> partition naming schemes without code modification. > >>> The UEFI spec does't use names during the update. That's an artificial > >>> limitation of your current implementation. The capsule code just looks > >>> at the index and the DFU command to update the proper partitions. > >> You're correct that the capsule code uses index and DFU commands. > >> However, in the current Qualcomm implementation, the DFU string itself > >> is built by searching for hardcoded partition names like 'uefi' in the > >> code: > >> if (!strncmp(info.name, "uefi_", strlen("uefi_"))) { > >> // Build DFU string with this partition > >> } > > I haven't looked at the code but that sounds like an implementation > > detail, that's used to *build* the dfu string dynamically for Qualcomm > > platforms. You'll still need that regardless of FIT/RAW capsules, > > since you have to define the dfu command. OTOH, you can statically > > define that dfu string per platform and remove that part. > > Okay, will look into this. Thanks /Ilias > > Thanks, > > Balaji > > > Cheers > > /Ilias > >> Regards, > >> > >> Balaji > >> > >>> Thanks > >>> /Ilias > >>>> Regards, > >>>> > >>>> Balaji > >>>> > >>>>>> Thanks > >>>>>> /Ilias > >>>>>>> Regards, > >>>>>>> > >>>>>>> Balaji > >>>>>>> > >>>>>>>>> Refactor qcom_configure_capsule_updates() to use compile-time > >>>>>>>>> mutual exclusivity between CONFIG_EFI_CAPSULE_FIRMWARE_FIT and > >>>>>>>>> CONFIG_EFI_CAPSULE_FIRMWARE_RAW using #elif preprocessor directives. > >>>>>>>>> > >>>>>>>>> Add board-specific FIT capsule GUIDs for QCS615, QCS6490, and Lemans > >>>>>>>>> platforms with automatic board detection from device tree compatible > >>>>>>>>> strings. Each board uses a unique GUID to prevent cross-board > >>>>>>>>> flashing accidents. > >>>>>>>>> > >>>>>>>>> The FIT implementation discovers all SCSI/eMMC partitions across > >>>>>>>>> multiple devices, applies A/B selection logic based on GPT vendor > >>>>>>>>> attributes, and generates a comprehensive DFU string for > >>>>>>>>> multi-partition updates. > >>>>>>>>> > >>>>>>>>> A single ESRT entry represents all partitions for simplified > >>>>>>>>> firmware > >>>>>>>>> management. > >>>>>>>>> > >>>>>>>>> Signed-off-by: Balaji Selvanathan > >>>>>>>>> <[email protected]> > >>>>>>>>> --- > >>>>>>>>> arch/arm/mach-snapdragon/capsule_update.c | 740 > >>>>>>>>> ++++++++++++++++++++++++++++-- > >>>>>>>>> arch/arm/mach-snapdragon/qcom-priv.h | 23 + > >>>>>>>>> 2 files changed, 712 insertions(+), 51 deletions(-) > >>>>>>>>> > >>>>>>>>> diff --git a/arch/arm/mach-snapdragon/capsule_update.c > >>>>>>>>> b/arch/arm/mach-snapdragon/capsule_update.c > >>>>>>>>> index 586682434b7..d803c46f38d 100644 > >>>>>>>>> --- a/arch/arm/mach-snapdragon/capsule_update.c > >>>>>>>>> +++ b/arch/arm/mach-snapdragon/capsule_update.c > >>>>>>>>> @@ -8,30 +8,55 @@ > >>>>>>>>> > >>>>>>>>> #define pr_fmt(fmt) "QCOM-FMP: " fmt > >>>>>>>>> > >>>>>>>>> -#include <dm/device.h> > >>>>>>>>> -#include <dm/uclass.h> > >>>>>>>>> +#include <command.h> > >>>>>>>>> #include <efi.h> > >>>>>>>>> #include <efi_loader.h> > >>>>>>>>> #include <malloc.h> > >>>>>>>>> #include <mmc.h> > >>>>>>>>> -#include <scsi.h> > >>>>>>>>> #include <part.h> > >>>>>>>>> +#include <scsi.h> > >>>>>>>>> +#include <dm/device.h> > >>>>>>>>> +#include <dm/uclass.h> > >>>>>>>>> #include <linux/err.h> > >>>>>>>>> - > >>>>>>>>> #include "qcom-priv.h" > >>>>>>>>> > >>>>>>>>> /* > >>>>>>>>> - * To handle different variants like chainloaded U-Boot here we > >>>>>>>>> need to > >>>>>>>>> - * build the fw_images array dynamically at runtime. These are the > >>>>>>>>> possible > >>>>>>>>> - * implementations: > >>>>>>>>> - * > >>>>>>>>> - * - Devices with U-Boot on the uefi_a/b partition > >>>>>>>>> - * - Devices with U-Boot on the boot (a/b) partition > >>>>>>>>> - * - Devices with U-Boot on the xbl (a/b) partition > >>>>>>>>> - * > >>>>>>>>> - * Which partition actually has U-Boot on it is determined based > >>>>>>>>> on the > >>>>>>>>> - * qcom_boot_source variable and additional logic in > >>>>>>>>> find_target_partition(). > >>>>>>>>> + * Capsule update support with conditional FIT vs RAW > >>>>>>>>> implementation: > >>>>>>>>> + * - FIT capsules: Comprehensive partition discovery with dynamic > >>>>>>>>> fw_images > >>>>>>>>> + * - RAW capsules: Existing single-partition approach with static > >>>>>>>>> fw_images > >>>>>>>>> */ > >>>>>>>>> + > >>>>>>>>> +#ifdef CONFIG_EFI_CAPSULE_FIRMWARE_FIT > >>>>>>>>> +#define MAX_DFU_STRING_SIZE 2048 > >>>>>>>>> +#define MAX_PARTITION_GROUPS 64 > >>>>>>>>> +#define MAX_PARTITIONS_PER_LUN 64 > >>>>>>>>> +#define MAX_PARTITIONS_TO_SCAN 128 > >>>>>>>>> +#define MAX_LUN_GROUPS 16 > >>>>>>>>> + > >>>>>>>>> +struct qcom_partition_info { > >>>>>>>>> + char name[32]; /* "uefi_a", "boot_b", etc. */ > >>>>>>>>> + char base_name[32]; /* "uefi", "boot", etc. */ > >>>>>>>>> + char slot_suffix[4]; /* "_a", "_b", or "" */ > >>>>>>>>> + int lun; /* SCSI LUN number */ > >>>>>>>>> + int partition_num; /* Partition number within LUN > >>>>>>>>> */ > >>>>>>>>> + bool is_active; /* From GPT vendor attributes */ > >>>>>>>>> + bool is_bootable; /* From GPT vendor attributes */ > >>>>>>>>> +}; > >>>>>>>>> + > >>>>>>>>> +struct partition_group { > >>>>>>>>> + char base_name[32]; > >>>>>>>>> + struct qcom_partition_info *a_slot; > >>>>>>>>> + struct qcom_partition_info *b_slot; > >>>>>>>>> + struct qcom_partition_info *no_slot; > >>>>>>>>> +}; > >>>>>>>>> + > >>>>>>>>> +struct lun_group { > >>>>>>>>> + int lun_number; > >>>>>>>>> + struct qcom_partition_info > >>>>>>>>> *partitions[MAX_PARTITIONS_PER_LUN]; /* Max partitions per LUN */ > >>>>>>>>> + int partition_count; > >>>>>>>>> +}; > >>>>>>>>> +#endif /* CONFIG_EFI_CAPSULE_FIRMWARE_FIT */ > >>>>>>>>> + > >>>>>>>>> struct efi_fw_image fw_images[] = { > >>>>>>>>> { > >>>>>>>>> .image_index = 1, > >>>>>>>>> @@ -39,18 +64,26 @@ struct efi_fw_image fw_images[] = { > >>>>>>>>> }; > >>>>>>>>> > >>>>>>>>> struct efi_capsule_update_info update_info = { > >>>>>>>>> - /* Filled in by configure_dfu_string() */ > >>>>>>>>> + /* Filled in by qcom_configure_capsule_updates() */ > >>>>>>>>> .dfu_string = NULL, > >>>>>>>>> .num_images = ARRAY_SIZE(fw_images), > >>>>>>>>> .images = fw_images, > >>>>>>>>> }; > >>>>>>>>> > >>>>>>>>> +#ifdef CONFIG_EFI_CAPSULE_FIRMWARE_RAW > >>>>>>>>> enum target_part_type { > >>>>>>>>> TARGET_PART_UEFI = 1, > >>>>>>>>> TARGET_PART_XBL, > >>>>>>>>> TARGET_PART_BOOT, > >>>>>>>>> }; > >>>>>>>>> > >>>>>>>>> +enum ab_slot { > >>>>>>>>> + SLOT_NONE, > >>>>>>>>> + SLOT_A, > >>>>>>>>> + SLOT_B, > >>>>>>>>> +}; > >>>>>>>>> +#endif /* CONFIG_EFI_CAPSULE_FIRMWARE_RAW */ > >>>>>>>>> + > >>>>>>>>> /* LSB first */ > >>>>>>>>> struct part_slot_status { > >>>>>>>>> u16: 2; > >>>>>>>>> @@ -61,26 +94,6 @@ struct part_slot_status { > >>>>>>>>> u16 tries_remaining : 4; > >>>>>>>>> }; > >>>>>>>>> > >>>>>>>>> -enum ab_slot { > >>>>>>>>> - SLOT_NONE, > >>>>>>>>> - SLOT_A, > >>>>>>>>> - SLOT_B, > >>>>>>>>> -}; > >>>>>>>>> - > >>>>>>>>> -static enum ab_slot get_part_slot(const char *partname) > >>>>>>>>> -{ > >>>>>>>>> - int len = strlen(partname); > >>>>>>>>> - > >>>>>>>>> - if (partname[len - 2] != '_') > >>>>>>>>> - return SLOT_NONE; > >>>>>>>>> - if (partname[len - 1] == 'a') > >>>>>>>>> - return SLOT_A; > >>>>>>>>> - if (partname[len - 1] == 'b') > >>>>>>>>> - return SLOT_B; > >>>>>>>>> - > >>>>>>>>> - return SLOT_NONE; > >>>>>>>>> -} > >>>>>>>>> - > >>>>>>>>> /* Shamelessly copied from lib/efi_loader/efi_device_path.c @ > >>>>>>>>> 33 */ > >>>>>>>>> /* > >>>>>>>>> * Determine if an MMC device is an SD card. > >>>>>>>>> @@ -98,6 +111,25 @@ static bool is_sd(struct blk_desc *desc) > >>>>>>>>> return IS_SD(mmc) != 0U; > >>>>>>>>> } > >>>>>>>>> > >>>>>>>>> +#ifdef CONFIG_EFI_CAPSULE_FIRMWARE_RAW > >>>>>>>>> +/* > >>>>>>>>> + * RAW Capsule Support > >>>>>>>>> + */ > >>>>>>>>> + > >>>>>>>>> +static enum ab_slot get_part_slot(const char *partname) > >>>>>>>>> +{ > >>>>>>>>> + int len = strlen(partname); > >>>>>>>>> + > >>>>>>>>> + if (partname[len - 2] != '_') > >>>>>>>>> + return SLOT_NONE; > >>>>>>>>> + if (partname[len - 1] == 'a') > >>>>>>>>> + return SLOT_A; > >>>>>>>>> + if (partname[len - 1] == 'b') > >>>>>>>>> + return SLOT_B; > >>>>>>>>> + > >>>>>>>>> + return SLOT_NONE; > >>>>>>>>> +} > >>>>>>>>> + > >>>>>>>>> /* > >>>>>>>>> * Determine which partition U-Boot is flashed to based on the > >>>>>>>>> boot source (ABL/XBL), > >>>>>>>>> * the slot status, and prioritizing the uefi partition over > >>>>>>>>> xbl if found. > >>>>>>>>> @@ -156,7 +188,7 @@ static int find_target_partition(int *devnum, > >>>>>>>>> enum uclass_id *uclass, > >>>>>>>>> * flags might not be set so we assume > >>>>>>>>> the A partition unless the B > >>>>>>>>> * partition is active. > >>>>>>>>> */ > >>>>>>>>> - if (!strncmp(info.name, "uefi", > >>>>>>>>> strlen("uefi"))) { > >>>>>>>>> + if (!strncmp(info.name, "uefi_", > >>>>>>>>> strlen("uefi_"))) { > >>>>>>>>> /* > >>>>>>>>> * If U-Boot was chainloaded > >>>>>>>>> somehow we can't be flashed to > >>>>>>>>> * the uefi partition > >>>>>>>>> @@ -263,7 +295,7 @@ static int find_target_partition(int *devnum, > >>>>>>>>> enum uclass_id *uclass, > >>>>>>>>> } > >>>>>>>>> > >>>>>>>>> /* Found no candidate partitions */ > >>>>>>>>> - return -1; > >>>>>>>>> + return -ENOENT; > >>>>>>>>> > >>>>>>>>> found: > >>>>>>>>> if (desc) { > >>>>>>>>> @@ -278,18 +310,7 @@ found: > >>>>>>>>> return partnum; > >>>>>>>>> } > >>>>>>>>> > >>>>>>>>> -/** > >>>>>>>>> - * qcom_configure_capsule_updates() - Configure the DFU string for > >>>>>>>>> capsule updates > >>>>>>>>> - * > >>>>>>>>> - * U-Boot is flashed to the boot partition on Qualcomm boards. In > >>>>>>>>> most cases there > >>>>>>>>> - * are two boot partitions, boot_a and boot_b. As we don't > >>>>>>>>> currently support doing > >>>>>>>>> - * full A/B updates, we only support updating the currently active > >>>>>>>>> boot partition. > >>>>>>>>> - * > >>>>>>>>> - * So we need to find the current slot suffix and the associated > >>>>>>>>> boot partition. > >>>>>>>>> - * We do this by looking for the boot partition that has the > >>>>>>>>> 'active' flag set > >>>>>>>>> - * in the GPT partition vendor attribute bits. > >>>>>>>>> - */ > >>>>>>>>> -void qcom_configure_capsule_updates(void) > >>>>>>>>> +static void configure_raw_capsule_updates(void) > >>>>>>>>> { > >>>>>>>>> int ret = 0, partnum = -1, devnum; > >>>>>>>>> static char dfu_string[32] = { 0 }; > >>>>>>>>> @@ -297,7 +318,6 @@ void qcom_configure_capsule_updates(void) > >>>>>>>>> enum uclass_id dev_uclass; > >>>>>>>>> > >>>>>>>>> if (IS_ENABLED(CONFIG_SCSI)) { > >>>>>>>>> - /* Scan for SCSI devices */ > >>>>>>>>> ret = scsi_scan(false); > >>>>>>>>> if (ret) { > >>>>>>>>> debug("Failed to scan SCSI devices: > >>>>>>>>> %d\n", ret); > >>>>>>>>> @@ -339,7 +359,625 @@ void qcom_configure_capsule_updates(void) > >>>>>>>>> debug("Unsupported storage uclass: %d\n", > >>>>>>>>> dev_uclass); > >>>>>>>>> return; > >>>>>>>>> } > >>>>>>>>> - log_debug("DFU string: '%s'\n", dfu_string); > >>>>>>>>> > >>>>>>>>> + log_debug("RAW DFU string: '%s'\n", dfu_string); > >>>>>>>>> + > >>>>>>>>> + /* Set RAW configuration state */ > >>>>>>>>> + update_info.dfu_string = dfu_string; > >>>>>>>>> + update_info.images = fw_images; > >>>>>>>>> + update_info.num_images = ARRAY_SIZE(fw_images); > >>>>>>>>> + > >>>>>>>>> + log_info("RAW capsule update configured (single partition: > >>>>>>>>> %s)\n", > >>>>>>>>> + target_part_type == TARGET_PART_UEFI ? "uefi" : > >>>>>>>>> + target_part_type == TARGET_PART_XBL ? "xbl" : > >>>>>>>>> "boot"); > >>>>>>>>> +} > >>>>>>>>> +#endif /* CONFIG_EFI_CAPSULE_FIRMWARE_RAW */ > >>>>>>>>> + > >>>>>>>>> +#ifdef CONFIG_EFI_CAPSULE_FIRMWARE_FIT > >>>>>>>>> +/* > >>>>>>>>> + * FIT Capsule Support - Implementation > >>>>>>>>> + */ > >>>>>>>>> + > >>>>>>>>> +static void parse_partition_name(const char *full_name, char > >>>>>>>>> *base_name, char *slot_suffix) > >>>>>>>>> +{ > >>>>>>>>> + char *underscore = strrchr(full_name, '_'); > >>>>>>>>> + > >>>>>>>>> + if (underscore && (strcmp(underscore, "_a") == 0 || > >>>>>>>>> strcmp(underscore, "_b") == 0)) { > >>>>>>>>> + /* Has A/B suffix */ > >>>>>>>>> + size_t base_len = underscore - full_name; > >>>>>>>>> + > >>>>>>>>> + strlcpy(base_name, full_name, base_len + 1); > >>>>>>>>> + strcpy(slot_suffix, underscore); > >>>>>>>>> + } else { > >>>>>>>>> + /* No A/B suffix */ > >>>>>>>>> + strcpy(base_name, full_name); > >>>>>>>>> + slot_suffix[0] = '\0'; > >>>>>>>>> + } > >>>>>>>>> +} > >>>>>>>>> + > >>>>>>>>> +static void parse_partition_info(struct qcom_partition_info *part, > >>>>>>>>> + struct disk_partition *info, > >>>>>>>>> + int lun, int partnum) > >>>>>>>>> +{ > >>>>>>>>> + struct part_slot_status *slot_status; > >>>>>>>>> + > >>>>>>>>> + strlcpy(part->name, info->name, sizeof(part->name)); > >>>>>>>>> + part->lun = lun; > >>>>>>>>> + part->partition_num = partnum; > >>>>>>>>> + > >>>>>>>>> + /* Parse slot status from GPT vendor attributes */ > >>>>>>>>> + slot_status = (struct part_slot_status *)&info->type_flags; > >>>>>>>>> + part->is_active = slot_status->active; > >>>>>>>>> + part->is_bootable = !slot_status->unbootable; > >>>>>>>>> + > >>>>>>>>> + /* Extract base name and slot suffix */ > >>>>>>>>> + parse_partition_name(part->name, part->base_name, > >>>>>>>>> part->slot_suffix); > >>>>>>>>> +} > >>>>>>>>> + > >>>>>>>>> +static struct partition_group *find_or_create_group(struct > >>>>>>>>> partition_group *groups, > >>>>>>>>> + int > >>>>>>>>> *group_count, > >>>>>>>>> + const char > >>>>>>>>> *base_name) > >>>>>>>>> +{ > >>>>>>>>> + /* Find existing group */ > >>>>>>>>> + for (int i = 0; i < *group_count; i++) { > >>>>>>>>> + if (strcmp(groups[i].base_name, base_name) == 0) > >>>>>>>>> + return &groups[i]; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + /* Create new group */ > >>>>>>>>> + if (*group_count >= MAX_PARTITION_GROUPS) { > >>>>>>>>> + log_err("Too many partition groups\n"); > >>>>>>>>> + return NULL; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + struct partition_group *new_group = &groups[*group_count]; > >>>>>>>>> + > >>>>>>>>> + strcpy(new_group->base_name, base_name); > >>>>>>>>> + new_group->a_slot = NULL; > >>>>>>>>> + new_group->b_slot = NULL; > >>>>>>>>> + new_group->no_slot = NULL; > >>>>>>>>> + > >>>>>>>>> + (*group_count)++; > >>>>>>>>> + return new_group; > >>>>>>>>> +} > >>>>>>>>> + > >>>>>>>>> +static struct qcom_partition_info *select_ab_target(struct > >>>>>>>>> qcom_partition_info *a_slot, > >>>>>>>>> + struct > >>>>>>>>> qcom_partition_info *b_slot) > >>>>>>>>> +{ > >>>>>>>>> + /* Priority: Active slot > A slot (fallback) */ > >>>>>>>>> + > >>>>>>>>> + if (a_slot && a_slot->is_active) { > >>>>>>>>> + log_debug("Selected %s (active)\n", a_slot->name); > >>>>>>>>> + return a_slot; > >>>>>>>>> + } > >>>>>>>>> + if (b_slot && b_slot->is_active) { > >>>>>>>>> + log_debug("Selected %s (active)\n", b_slot->name); > >>>>>>>>> + return b_slot; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + /* Both inactive - prefer A slot as fallback */ > >>>>>>>>> + struct qcom_partition_info *fallback = a_slot ? a_slot : > >>>>>>>>> b_slot; > >>>>>>>>> + > >>>>>>>>> + if (fallback) > >>>>>>>>> + log_debug("Selected %s (fallback - both > >>>>>>>>> inactive)\n", fallback->name); > >>>>>>>>> + return fallback; > >>>>>>>>> +} > >>>>>>>>> + > >>>>>>>>> +static int discover_all_partitions(struct qcom_partition_info > >>>>>>>>> **all_parts, int *all_count) > >>>>>>>>> +{ > >>>>>>>>> + struct udevice *dev; > >>>>>>>>> + struct blk_desc *desc; > >>>>>>>>> + struct qcom_partition_info *partition_list; > >>>>>>>>> + int partition_count = 0; > >>>>>>>>> + int max_partitions = 256; > >>>>>>>>> + bool have_ufs = false; > >>>>>>>>> + > >>>>>>>>> + /* Allocate partition list */ > >>>>>>>>> + partition_list = calloc(max_partitions, sizeof(struct > >>>>>>>>> qcom_partition_info)); > >>>>>>>>> + if (!partition_list) { > >>>>>>>>> + log_err("Failed to allocate partition list\n"); > >>>>>>>>> + return -ENOMEM; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + if (IS_ENABLED(CONFIG_SCSI)) { > >>>>>>>>> + if (scsi_scan(false)) { > >>>>>>>>> + log_debug("Failed to scan SCSI devices\n"); > >>>>>>>>> + free(partition_list); > >>>>>>>>> + return -EIO; > >>>>>>>>> + } > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + /* > >>>>>>>>> + * Check to see if we have UFS storage, if so firmware MUST > >>>>>>>>> be on it and we can skip > >>>>>>>>> + * all non-UFS block devices > >>>>>>>>> + */ > >>>>>>>>> + uclass_foreach_dev_probe(UCLASS_UFS, dev) { > >>>>>>>>> + have_ufs = true; > >>>>>>>>> + break; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + /* Discover partitions with UFS-priority logic */ > >>>>>>>>> + uclass_foreach_dev_probe(UCLASS_BLK, dev) { > >>>>>>>>> + if (device_get_uclass_id(dev) != UCLASS_BLK) > >>>>>>>>> + continue; > >>>>>>>>> + > >>>>>>>>> + desc = dev_get_uclass_plat(dev); > >>>>>>>>> + if (!desc) > >>>>>>>>> + continue; > >>>>>>>>> + > >>>>>>>>> + if (have_ufs) { > >>>>>>>>> + if > >>>>>>>>> (device_get_uclass_id(dev->parent->parent) != UCLASS_UFS) > >>>>>>>>> + continue; > >>>>>>>>> + } else { > >>>>>>>>> + /* If we don't have UFS, look at eMMC (but > >>>>>>>>> skip SD cards) */ > >>>>>>>>> + if (desc->uclass_id == UCLASS_MMC) { > >>>>>>>>> + if (IS_ENABLED(CONFIG_MMC) && > >>>>>>>>> is_sd(desc)) { > >>>>>>>>> + log_debug("Skipped SD-Card > >>>>>>>>> (devnum %d)\n", desc->devnum); > >>>>>>>>> + continue; > >>>>>>>>> + } > >>>>>>>>> + } else if (desc->uclass_id != UCLASS_SCSI) { > >>>>>>>>> + /* Not MMC and not SCSI, skip it */ > >>>>>>>>> + continue; > >>>>>>>>> + } > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + int lun = desc->devnum; > >>>>>>>>> + > >>>>>>>>> + /* Scan all partitions on this device */ > >>>>>>>>> + for (int partnum = 1; partnum <= > >>>>>>>>> MAX_PARTITIONS_TO_SCAN; partnum++) { > >>>>>>>>> + struct disk_partition info; > >>>>>>>>> + > >>>>>>>>> + if (part_get_info(desc, partnum, &info) != > >>>>>>>>> 0) > >>>>>>>>> + break; > >>>>>>>>> + > >>>>>>>>> + if (partition_count >= max_partitions) { > >>>>>>>>> + log_warning("Too many partitions > >>>>>>>>> discovered, truncating at %d\n", > >>>>>>>>> + max_partitions); > >>>>>>>>> + break; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + /* Parse and store partition info */ > >>>>>>>>> + > >>>>>>>>> parse_partition_info(&partition_list[partition_count], &info, lun, > >>>>>>>>> partnum); > >>>>>>>>> + partition_count++; > >>>>>>>>> + } > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + *all_parts = partition_list; > >>>>>>>>> + *all_count = partition_count; > >>>>>>>>> + > >>>>>>>>> + log_debug("Discovered %d partitions across all %s > >>>>>>>>> devices\n", > >>>>>>>>> + partition_count, have_ufs ? "UFS" : "eMMC"); > >>>>>>>>> + return 0; > >>>>>>>>> +} > >>>>>>>>> + > >>>>>>>>> +static int select_target_partitions(struct qcom_partition_info > >>>>>>>>> *all_parts, int all_count, > >>>>>>>>> + struct qcom_partition_info > >>>>>>>>> **selected_parts, > >>>>>>>>> + int *selected_count) > >>>>>>>>> +{ > >>>>>>>>> + struct partition_group groups[MAX_PARTITION_GROUPS]; > >>>>>>>>> + struct qcom_partition_info *target_list; > >>>>>>>>> + int group_count = 0; > >>>>>>>>> + int target_count = 0; > >>>>>>>>> + > >>>>>>>>> + memset(groups, 0, sizeof(groups)); > >>>>>>>>> + > >>>>>>>>> + /* Allocate target list */ > >>>>>>>>> + target_list = calloc(all_count, sizeof(struct > >>>>>>>>> qcom_partition_info)); > >>>>>>>>> + if (!target_list) { > >>>>>>>>> + log_err("Failed to allocate target partition > >>>>>>>>> list\n"); > >>>>>>>>> + return -ENOMEM; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + /* Group partitions by base name */ > >>>>>>>>> + for (int i = 0; i < all_count; i++) { > >>>>>>>>> + struct qcom_partition_info *part = &all_parts[i]; > >>>>>>>>> + struct partition_group *group = > >>>>>>>>> find_or_create_group(groups, &group_count, > >>>>>>>>> + > >>>>>>>>> part->base_name); > >>>>>>>>> + > >>>>>>>>> + if (!group) { > >>>>>>>>> + log_err("Failed to create group for %s\n", > >>>>>>>>> part->base_name); > >>>>>>>>> + continue; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + if (strcmp(part->slot_suffix, "_a") == 0) { > >>>>>>>>> + if (!group->a_slot) { > >>>>>>>>> + group->a_slot = part; > >>>>>>>>> + } else { > >>>>>>>>> + log_info("Duplicate A-slot > >>>>>>>>> partition detected\n"); > >>>>>>>>> + log_info(" Keeping: %s (LUN %d, > >>>>>>>>> partition %d) [first discovered]\n", > >>>>>>>>> + group->a_slot->name, > >>>>>>>>> group->a_slot->lun, > >>>>>>>>> + > >>>>>>>>> group->a_slot->partition_num); > >>>>>>>>> + log_info(" Ignoring: %s (LUN %d, > >>>>>>>>> partition %d) [duplicate]\n", > >>>>>>>>> + part->name, part->lun, > >>>>>>>>> part->partition_num); > >>>>>>>>> + } > >>>>>>>>> + } else if (strcmp(part->slot_suffix, "_b") == 0) { > >>>>>>>>> + if (!group->b_slot) { > >>>>>>>>> + group->b_slot = part; > >>>>>>>>> + } else { > >>>>>>>>> + log_info("Duplicate B-slot > >>>>>>>>> partition detected\n"); > >>>>>>>>> + log_info(" Keeping: %s (LUN %d, > >>>>>>>>> partition %d) [first discovered]\n", > >>>>>>>>> + group->b_slot->name, > >>>>>>>>> group->b_slot->lun, > >>>>>>>>> + > >>>>>>>>> group->b_slot->partition_num); > >>>>>>>>> + log_info(" Ignoring: %s (LUN %d, > >>>>>>>>> partition %d) [duplicate]\n", > >>>>>>>>> + part->name, part->lun, > >>>>>>>>> part->partition_num); > >>>>>>>>> + } > >>>>>>>>> + } else { > >>>>>>>>> + if (!group->no_slot) { > >>>>>>>>> + group->no_slot = part; > >>>>>>>>> + } else { > >>>>>>>>> + log_info("Duplicate non-A/B > >>>>>>>>> partition detected\n"); > >>>>>>>>> + log_info(" Keeping: %s (LUN %d, > >>>>>>>>> partition %d) [first discovered]\n", > >>>>>>>>> + group->no_slot->name, > >>>>>>>>> group->no_slot->lun, > >>>>>>>>> + > >>>>>>>>> group->no_slot->partition_num); > >>>>>>>>> + log_info(" Ignoring: %s (LUN %d, > >>>>>>>>> partition %d) [duplicate]\n", > >>>>>>>>> + part->name, part->lun, > >>>>>>>>> part->partition_num); > >>>>>>>>> + } > >>>>>>>>> + } > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + log_debug("Created %d partition groups for selection\n", > >>>>>>>>> group_count); > >>>>>>>>> + > >>>>>>>>> + /* Select target partition for each group */ > >>>>>>>>> + for (int i = 0; i < group_count; i++) { > >>>>>>>>> + struct partition_group *group = &groups[i]; > >>>>>>>>> + struct qcom_partition_info *target = NULL; > >>>>>>>>> + > >>>>>>>>> + if (group->no_slot) { > >>>>>>>>> + /* Non-A/B partition */ > >>>>>>>>> + target = group->no_slot; > >>>>>>>>> + log_debug("Group %s: selected non-A/B > >>>>>>>>> partition %s\n", > >>>>>>>>> + group->base_name, target->name); > >>>>>>>>> + } else { > >>>>>>>>> + /* A/B partition - apply selection logic */ > >>>>>>>>> + target = select_ab_target(group->a_slot, > >>>>>>>>> group->b_slot); > >>>>>>>>> + if (target) { > >>>>>>>>> + log_debug("Group %s: selected %s > >>>>>>>>> from A/B pair\n", > >>>>>>>>> + group->base_name, > >>>>>>>>> target->name); > >>>>>>>>> + } > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + if (target) { > >>>>>>>>> + /* Copy selected partition to target list */ > >>>>>>>>> + memcpy(&target_list[target_count], target, > >>>>>>>>> + sizeof(struct qcom_partition_info)); > >>>>>>>>> + target_count++; > >>>>>>>>> + } else { > >>>>>>>>> + log_info("No target selected for group > >>>>>>>>> %s\n", group->base_name); > >>>>>>>>> + } > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + *selected_parts = target_list; > >>>>>>>>> + *selected_count = target_count; > >>>>>>>>> + > >>>>>>>>> + log_debug("Selected %d target partitions from %d > >>>>>>>>> discovered\n", target_count, all_count); > >>>>>>>>> + return 0; > >>>>>>>>> +} > >>>>>>>>> + > >>>>>>>>> +static int group_partitions_by_lun(struct qcom_partition_info > >>>>>>>>> *selected_parts, int selected_count, > >>>>>>>>> + struct lun_group **lun_groups, > >>>>>>>>> int *group_count) > >>>>>>>>> +{ > >>>>>>>>> + struct lun_group *groups; > >>>>>>>>> + int max_groups = MAX_LUN_GROUPS; > >>>>>>>>> + int current_groups = 0; > >>>>>>>>> + > >>>>>>>>> + /* Allocate LUN groups array */ > >>>>>>>>> + groups = calloc(max_groups, sizeof(struct lun_group)); > >>>>>>>>> + if (!groups) { > >>>>>>>>> + log_err("Failed to allocate LUN groups array\n"); > >>>>>>>>> + return -ENOMEM; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + /* Group partitions by LUN */ > >>>>>>>>> + for (int i = 0; i < selected_count; i++) { > >>>>>>>>> + struct qcom_partition_info *part = > >>>>>>>>> &selected_parts[i]; > >>>>>>>>> + struct lun_group *target_group = NULL; > >>>>>>>>> + > >>>>>>>>> + /* Find existing group for this LUN */ > >>>>>>>>> + for (int j = 0; j < current_groups; j++) { > >>>>>>>>> + if (groups[j].lun_number == part->lun) { > >>>>>>>>> + target_group = &groups[j]; > >>>>>>>>> + break; > >>>>>>>>> + } > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + /* Create new group if not found */ > >>>>>>>>> + if (!target_group) { > >>>>>>>>> + if (current_groups >= max_groups) { > >>>>>>>>> + log_err("Too many LUN groups (max > >>>>>>>>> %d)\n", max_groups); > >>>>>>>>> + free(groups); > >>>>>>>>> + return -ENOSPC; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + target_group = &groups[current_groups]; > >>>>>>>>> + target_group->lun_number = part->lun; > >>>>>>>>> + target_group->partition_count = 0; > >>>>>>>>> + current_groups++; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + /* Add partition to group */ > >>>>>>>>> + if (target_group->partition_count >= 64) { > >>>>>>>>> + log_err("Too many partitions in LUN %d (max > >>>>>>>>> 64)\n", part->lun); > >>>>>>>>> + free(groups); > >>>>>>>>> + return -ENOSPC; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + > >>>>>>>>> target_group->partitions[target_group->partition_count] = part; > >>>>>>>>> + target_group->partition_count++; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + /* Sort groups by LUN number for consistent output > >>>>>>>>> */ > >>>>>>>>> + for (int i = 0; i < current_groups - 1; i++) { > >>>>>>>>> + for (int j = i + 1; j < current_groups; > >>>>>>>>> j++) { > >>>>>>>>> + if (groups[i].lun_number > > >>>>>>>>> groups[j].lun_number) { > >>>>>>>>> + struct lun_group temp = > >>>>>>>>> groups[i]; > >>>>>>>>> + > >>>>>>>>> + groups[i] = groups[j]; > >>>>>>>>> + groups[j] = temp; > >>>>>>>>> + } > >>>>>>>>> + } > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + *lun_groups = groups; > >>>>>>>>> + *group_count = current_groups; > >>>>>>>>> + > >>>>>>>>> + log_debug("Grouped %d partitions into %d LUN groups\n", > >>>>>>>>> selected_count, current_groups); > >>>>>>>>> + return 0; > >>>>>>>>> +} > >>>>>>>>> + > >>>>>>>>> +static int generate_dfu_string(struct qcom_partition_info > >>>>>>>>> *selected_parts, int selected_count, > >>>>>>>>> + char *dfu_string, size_t buffer_size) > >>>>>>>>> +{ > >>>>>>>>> + struct lun_group *lun_groups = NULL; > >>>>>>>>> + struct udevice *dev; > >>>>>>>>> + struct blk_desc *desc; > >>>>>>>>> + int group_count = 0; > >>>>>>>>> + char *dfu_ptr = dfu_string; > >>>>>>>>> + int remaining = buffer_size; > >>>>>>>>> + int ret; > >>>>>>>>> + bool is_mmc = false; > >>>>>>>>> + > >>>>>>>>> + /* Clear the buffer */ > >>>>>>>>> + memset(dfu_string, 0, buffer_size); > >>>>>>>>> + > >>>>>>>>> + /* Determine storage type by checking the first partition's > >>>>>>>>> device */ > >>>>>>>>> + if (selected_count > 0) { > >>>>>>>>> + uclass_foreach_dev_probe(UCLASS_BLK, dev) { > >>>>>>>>> + if (device_get_uclass_id(dev) != UCLASS_BLK) > >>>>>>>>> + continue; > >>>>>>>>> + > >>>>>>>>> + desc = dev_get_uclass_plat(dev); > >>>>>>>>> + if (!desc) > >>>>>>>>> + continue; > >>>>>>>>> + > >>>>>>>>> + if (desc->devnum == selected_parts[0].lun) { > >>>>>>>>> + if (desc->uclass_id == UCLASS_MMC) { > >>>>>>>>> + is_mmc = true; > >>>>>>>>> + log_debug("Detected > >>>>>>>>> MMC/eMMC storage for DFU string generation\n"); > >>>>>>>>> + } else if (desc->uclass_id == > >>>>>>>>> UCLASS_SCSI) { > >>>>>>>>> + is_mmc = false; > >>>>>>>>> + log_debug("Detected > >>>>>>>>> SCSI/UFS storage for DFU string generation\n"); > >>>>>>>>> + } > >>>>>>>>> + break; > >>>>>>>>> + } > >>>>>>>>> + } > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + /* Group partitions by LUN/device */ > >>>>>>>>> + ret = group_partitions_by_lun(selected_parts, > >>>>>>>>> selected_count, &lun_groups, &group_count); > >>>>>>>>> + if (ret != 0) { > >>>>>>>>> + log_err("Failed to group partitions by LUN: %d\n", > >>>>>>>>> ret); > >>>>>>>>> + return ret; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + /* Generate DFU string with appropriate format for storage > >>>>>>>>> type */ > >>>>>>>>> + for (int i = 0; i < group_count; i++) { > >>>>>>>>> + struct lun_group *group = &lun_groups[i]; > >>>>>>>>> + int written; > >>>>>>>>> + > >>>>>>>>> + /* Add device group separator for non-first groups > >>>>>>>>> */ > >>>>>>>>> + if (i > 0) { > >>>>>>>>> + written = snprintf(dfu_ptr, remaining, "&"); > >>>>>>>>> + dfu_ptr += written; > >>>>>>>>> + remaining -= written; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + if (is_mmc) { > >>>>>>>>> + /* MMC format: "mmc X=" */ > >>>>>>>>> + written = snprintf(dfu_ptr, remaining, "mmc > >>>>>>>>> %d=", group->lun_number); > >>>>>>>>> + } else { > >>>>>>>>> + /* SCSI format: "scsi X=" */ > >>>>>>>>> + written = snprintf(dfu_ptr, remaining, > >>>>>>>>> "scsi %d=", group->lun_number); > >>>>>>>>> + } > >>>>>>>>> + dfu_ptr += written; > >>>>>>>>> + remaining -= written; > >>>>>>>>> + > >>>>>>>>> + /* Add partitions within this device group */ > >>>>>>>>> + for (int j = 0; j < group->partition_count; j++) { > >>>>>>>>> + struct qcom_partition_info *part = > >>>>>>>>> group->partitions[j]; > >>>>>>>>> + > >>>>>>>>> + /* Add partition separator for non-first > >>>>>>>>> partitions in group */ > >>>>>>>>> + if (j > 0) { > >>>>>>>>> + written = snprintf(dfu_ptr, > >>>>>>>>> remaining, ";"); > >>>>>>>>> + dfu_ptr += written; > >>>>>>>>> + remaining -= written; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + if (is_mmc) { > >>>>>>>>> + /* MMC format: "partition_name part > >>>>>>>>> dev_num partition_num" */ > >>>>>>>>> + written = snprintf(dfu_ptr, > >>>>>>>>> remaining, "%s part %d %d", > >>>>>>>>> + part->name, > >>>>>>>>> group->lun_number, part->partition_num); > >>>>>>>>> + } else { > >>>>>>>>> + /* SCSI format: "partition_name > >>>>>>>>> part partition_num" */ > >>>>>>>>> + written = snprintf(dfu_ptr, > >>>>>>>>> remaining, "%s part %d", > >>>>>>>>> + part->name, > >>>>>>>>> part->partition_num); > >>>>>>>>> + } > >>>>>>>>> + dfu_ptr += written; > >>>>>>>>> + remaining -= written; > >>>>>>>>> + > >>>>>>>>> + if (remaining <= 10) { > >>>>>>>>> + log_err("DFU string buffer overflow > >>>>>>>>> at partition %s\n", part->name); > >>>>>>>>> + free(lun_groups); > >>>>>>>>> + return -ENOSPC; > >>>>>>>>> + } > >>>>>>>>> + } > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + /* Clean up */ > >>>>>>>>> + free(lun_groups); > >>>>>>>>> + > >>>>>>>>> + log_debug("Generated %s DFU string (%zu chars): %s\n", > >>>>>>>>> + is_mmc ? "MMC" : "SCSI", strlen(dfu_string), > >>>>>>>>> dfu_string); > >>>>>>>>> + return 0; > >>>>>>>>> +} > >>>>>>>>> + > >>>>>>>>> +/** > >>>>>>>>> + * get_board_fit_capsule_guid - Get board-specific FIT capsule GUID > >>>>>>>>> + * > >>>>>>>>> + * Detect the board type from device tree and return the > >>>>>>>>> appropriate GUID > >>>>>>>>> + * for FIT capsule updates. > >>>>>>>>> + * > >>>>>>>>> + * @guid: Pointer to store the GUID > >>>>>>>>> + * Return: 0 on success, negative error code on failure > >>>>>>>>> + */ > >>>>>>>>> +static int get_board_fit_capsule_guid(efi_guid_t *guid) > >>>>>>>>> +{ > >>>>>>>>> + const char *compatible; > >>>>>>>>> + > >>>>>>>>> + if (!guid) > >>>>>>>>> + return -EINVAL; > >>>>>>>>> + > >>>>>>>>> + compatible = ofnode_read_string(ofnode_root(), > >>>>>>>>> "compatible"); > >>>>>>>>> + if (!compatible) { > >>>>>>>>> + log_err("Failed to read board compatible string\n"); > >>>>>>>>> + return -ENODEV; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + /* Check for QCS615 or Talos */ > >>>>>>>>> + if (strstr(compatible, "qcs615") || strstr(compatible, > >>>>>>>>> "talos")) { > >>>>>>>>> + log_debug("Detected QCS615/Talos board\n"); > >>>>>>>>> + *guid = (efi_guid_t)QCOM_QCS615_FIT_CAPSULE_GUID; > >>>>>>>>> + return 0; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + /* Check for QCS6490 */ > >>>>>>>>> + if (strstr(compatible, "qcs6490")) { > >>>>>>>>> + log_debug("Detected QCS6490 board\n"); > >>>>>>>>> + *guid = (efi_guid_t)QCOM_QCS6490_FIT_CAPSULE_GUID; > >>>>>>>>> + return 0; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + /* Check for Lemans */ > >>>>>>>>> + if (strstr(compatible, "lemans") || strstr(compatible, > >>>>>>>>> "qcs9100")) { > >>>>>>>>> + log_debug("Detected Lemans board\n"); > >>>>>>>>> + *guid = (efi_guid_t)QCOM_LEMANS_FIT_CAPSULE_GUID; > >>>>>>>>> + return 0; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + log_err("Unsupported board for capsule updates: %s\n", > >>>>>>>>> compatible); > >>>>>>>>> + return -EINVAL; > >>>>>>>>> +} > >>>>>>>>> + > >>>>>>>>> +/* > >>>>>>>>> + * For creating FIT-based capsule images from FvUpdate.xml files, > >>>>>>>>> see: > >>>>>>>>> + * - Tool: tools/fvupdate_to_fit.py > >>>>>>>>> + * - Documentation: doc/develop/fvupdate_to_fit.rst > >>>>>>>>> + */ > >>>>>>>>> +static void configure_fit_capsule_updates(void) > >>>>>>>>> +{ > >>>>>>>>> + struct qcom_partition_info *all_partitions = NULL; > >>>>>>>>> + struct qcom_partition_info *selected_partitions = NULL; > >>>>>>>>> + int all_count = 0, selected_count = 0; > >>>>>>>>> + static char dfu_string[MAX_DFU_STRING_SIZE] = { 0 }; > >>>>>>>>> + static struct efi_fw_image single_fw_image; > >>>>>>>>> + efi_guid_t board_guid; > >>>>>>>>> + int ret; > >>>>>>>>> + > >>>>>>>>> + /* Step 1: Discover all partitions across all SCSI LUNs */ > >>>>>>>>> + ret = discover_all_partitions(&all_partitions, &all_count); > >>>>>>>>> + if (ret != 0) { > >>>>>>>>> + log_err("Failed to discover SCSI partitions: %d\n", > >>>>>>>>> ret); > >>>>>>>>> + return; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + if (all_count == 0) { > >>>>>>>>> + log_warning("No SCSI partitions discovered\n"); > >>>>>>>>> + goto cleanup; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + /* Step 2: Apply A/B selection logic to choose target > >>>>>>>>> partitions */ > >>>>>>>>> + ret = select_target_partitions(all_partitions, all_count, > >>>>>>>>> + &selected_partitions, > >>>>>>>>> &selected_count); > >>>>>>>>> + if (ret != 0) { > >>>>>>>>> + log_err("Failed to select target partitions: %d\n", > >>>>>>>>> ret); > >>>>>>>>> + goto cleanup; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + if (selected_count == 0) { > >>>>>>>>> + log_warning("No target partitions selected\n"); > >>>>>>>>> + goto cleanup; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + /* Step 3: Generate DFU string from selected partitions */ > >>>>>>>>> + ret = generate_dfu_string(selected_partitions, > >>>>>>>>> selected_count, > >>>>>>>>> + dfu_string, sizeof(dfu_string)); > >>>>>>>>> + if (ret != 0) { > >>>>>>>>> + log_err("Failed to generate DFU string: %d\n", ret); > >>>>>>>>> + goto cleanup; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + /* Step 4: Get board-specific GUID */ > >>>>>>>>> + ret = get_board_fit_capsule_guid(&board_guid); > >>>>>>>>> + if (ret != 0) { > >>>>>>>>> + log_err("Failed to get board-specific GUID: %d\n", > >>>>>>>>> ret); > >>>>>>>>> + goto cleanup; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + /* Step 5: Create SINGLE fw_image entry for ESRT */ > >>>>>>>>> + memset(&single_fw_image, 0, sizeof(single_fw_image)); > >>>>>>>>> + single_fw_image.fw_name = QCOM_FIT_CAPSULE_NAME; /* Same > >>>>>>>>> name for all boards */ > >>>>>>>>> + single_fw_image.image_index = 1; > >>>>>>>>> + single_fw_image.image_type_id = board_guid; > >>>>>>>>> + > >>>>>>>>> + /* Step 6: Configure update_info */ > >>>>>>>>> update_info.dfu_string = dfu_string; > >>>>>>>>> + update_info.images = &single_fw_image; > >>>>>>>>> + update_info.num_images = 1; > >>>>>>>>> + > >>>>>>>>> + log_info("FIT capsule configured successfully:\n"); > >>>>>>>>> + log_info(" Name: %ls\n", QCOM_FIT_CAPSULE_NAME); > >>>>>>>>> + log_info(" GUID: %pUl\n", &board_guid); > >>>>>>>>> + log_info(" Partitions in DFU string: %d\n", > >>>>>>>>> selected_count); > >>>>>>>>> + log_info(" ESRT entries: 1 (single entry for all > >>>>>>>>> partitions)\n"); > >>>>>>>>> + > >>>>>>>>> +cleanup: > >>>>>>>>> + free(all_partitions); > >>>>>>>>> + free(selected_partitions); > >>>>>>>>> +} > >>>>>>>>> +#endif /* CONFIG_EFI_CAPSULE_FIRMWARE_FIT */ > >>>>>>>>> + > >>>>>>>>> +/** > >>>>>>>>> + * qcom_configure_capsule_updates() - Configure capsule updates > >>>>>>>>> + * > >>>>>>>>> + * Configures either FIT or RAW capsule updates based on > >>>>>>>>> compile-time configuration. > >>>>>>>>> + */ > >>>>>>>>> +void qcom_configure_capsule_updates(void) > >>>>>>>>> +{ > >>>>>>>>> +#if defined(CONFIG_EFI_CAPSULE_FIRMWARE_FIT) > >>>>>>>>> + log_info("Configuring FIT capsule updates\n"); > >>>>>>>>> + configure_fit_capsule_updates(); > >>>>>>>>> +#elif defined(CONFIG_EFI_CAPSULE_FIRMWARE_RAW) > >>>>>>>>> + log_info("Configuring RAW capsule updates\n"); > >>>>>>>>> + configure_raw_capsule_updates(); > >>>>>>>>> +#else > >>>>>>>>> + log_warning("No capsule firmware configuration enabled\n"); > >>>>>>>>> +#endif > >>>>>>>>> + > >>>>>>>>> + /* Final state logging */ > >>>>>>>>> + if (update_info.dfu_string) { > >>>>>>>>> + log_info("Capsule update configured successfully > >>>>>>>>> with %d image(s)\n", > >>>>>>>>> + update_info.num_images); > >>>>>>>>> + } else { > >>>>>>>>> + log_warning("Capsule update configuration > >>>>>>>>> failed\n"); > >>>>>>>>> + } > >>>>>>>>> } > >>>>>>>>> + > >>>>>>>>> diff --git a/arch/arm/mach-snapdragon/qcom-priv.h > >>>>>>>>> b/arch/arm/mach-snapdragon/qcom-priv.h > >>>>>>>>> index b8bf574e8bb..d664c22ae96 100644 > >>>>>>>>> --- a/arch/arm/mach-snapdragon/qcom-priv.h > >>>>>>>>> +++ b/arch/arm/mach-snapdragon/qcom-priv.h > >>>>>>>>> @@ -18,6 +18,29 @@ enum qcom_boot_source { > >>>>>>>>> extern enum qcom_boot_source qcom_boot_source; > >>>>>>>>> > >>>>>>>>> #if IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) > >>>>>>>>> +/* > >>>>>>>>> + * Capsule Update GUIDs for FIT capsules > >>>>>>>>> + * Each board has a unique GUID to prevent cross-board flashing > >>>>>>>>> + */ > >>>>>>>>> + > >>>>>>>>> +/* QCS615 FIT Capsule GUID: 9fd379d2-670e-4bb3-86a1-40497e6e17b0 */ > >>>>>>>>> +#define QCOM_QCS615_FIT_CAPSULE_GUID \ > >>>>>>>>> + EFI_GUID(0x9fd379d2, 0x670e, 0x4bb3, 0x86, 0xa1, \ > >>>>>>>>> + 0x40, 0x49, 0x7e, 0x6e, 0x17, 0xb0) > >>>>>>>>> + > >>>>>>>>> +/* QCS6490 FIT Capsule GUID: 6f25bfd2-a165-468b-980f-ac51a0a45c52 > >>>>>>>>> */ > >>>>>>>>> +#define QCOM_QCS6490_FIT_CAPSULE_GUID \ > >>>>>>>>> + EFI_GUID(0x6f25bfd2, 0xa165, 0x468b, 0x98, 0x0f, \ > >>>>>>>>> + 0xac, 0x51, 0xa0, 0xa4, 0x5c, 0x52) > >>>>>>>>> + > >>>>>>>>> +/* Lemans FIT Capsule GUID: 78462415-6133-431c-9fae-48f2bafd5c71 */ > >>>>>>>>> +#define QCOM_LEMANS_FIT_CAPSULE_GUID \ > >>>>>>>>> + EFI_GUID(0x78462415, 0x6133, 0x431c, 0x9f, 0xae, \ > >>>>>>>>> + 0x48, 0xf2, 0xba, 0xfd, 0x5c, 0x71) > >>>>>>>>> + > >>>>>>>>> +/* Common name for FIT capsule (same for all boards) */ > >>>>>>>>> +#define QCOM_FIT_CAPSULE_NAME u"QCOM_FIT_CAPSULE" > >>>>>>>>> + > >>>>>>>>> void qcom_configure_capsule_updates(void); > >>>>>>>>> #else > >>>>>>>>> void qcom_configure_capsule_updates(void) {} > >>>>>>>>> > >>>>>>>>> -- > >>>>>>>>> 2.34.1 > >>>>>>>>>

