Add create_multi_payload_fwbin() to bundle up to 16 firmware images into a single capsule file. Only normal blob payloads are supported for now; signing and the config file parser integration will follow in later patches.
Signed-off-by: Balaji Selvanathan <[email protected]> --- tools/eficapsule.h | 27 ++++++++ tools/mkeficapsule.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 201 insertions(+) diff --git a/tools/eficapsule.h b/tools/eficapsule.h index 6e27ed05144..27ccb66c3e8 100644 --- a/tools/eficapsule.h +++ b/tools/eficapsule.h @@ -179,6 +179,18 @@ struct efi_capsule_params { enum capsule_type capsule; }; +#define MAX_PAYLOADS_PER_CAPSULE 16 + +/** + * struct efi_capsule_params_array - Array of capsule parameters + * @count: Number of parameters in the array + * @params: Array of pointers to capsule parameters + */ +struct efi_capsule_params_array { + int count; + struct efi_capsule_params *params[MAX_PAYLOADS_PER_CAPSULE]; +}; + /** * capsule_with_cfg_file() - Generate capsule from config file * @cfg_file: Path to the config file @@ -235,6 +247,21 @@ int create_fwbin(char *path, char *bin, efi_guid_t *guid, uint64_t mcount, char *privkey_file, char *cert_file, uint16_t oemflags); +/** + * create_multi_payload_fwbin - create a multi-payload uefi capsule file + * @path: Path to a created capsule file + * @params_array: Array of capsule parameters for multiple payloads + * + * This function creates a UEFI capsule file with multiple payloads. + * All payloads must be normal blob type capsules. + * + * Return: + * * 0 - on success + * * -1 - on failure + */ +int create_multi_payload_fwbin(char *path, + struct efi_capsule_params_array *params_array); + /** * print_usage() - Print the command usage string * diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index e778343a296..03a9a44e1bd 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -725,6 +725,180 @@ static void print_guid(void *ptr) printf("%s\n", buf); } +/** + * create_multi_payload_fwbin - create a multi-payload uefi capsule file + * @path: Path to a created capsule file + * @params_array: Array of capsule parameters for multiple payloads + * + * This function creates a UEFI capsule file with multiple payloads. + * All payloads must be normal blob type capsules. + * + * Return: + * * 0 - on success + * * -1 - on failure + */ +int create_multi_payload_fwbin(char *path, + struct efi_capsule_params_array *params_array) +{ + struct efi_capsule_header header; + struct efi_firmware_management_capsule_header capsule; + struct efi_firmware_management_capsule_image_header *image_headers; + FILE *f = NULL; + uint8_t **payload_data = NULL; + off_t *payload_sizes = NULL; + uint64_t *offsets = NULL; + uint64_t offset; + size_t capsule_size; + int i, ret = -1; + struct fmp_payload_header payload_header; + + /* Validate all payloads are normal blobs */ + for (i = 0; i < params_array->count; i++) { + if (params_array->params[i]->capsule != CAPSULE_NORMAL_BLOB) { + fprintf(stderr, "Multi-payload capsules only support normal blob type\n"); + return -1; + } + if (params_array->params[i]->privkey_file || params_array->params[i]->cert_file) { + fprintf(stderr, "Multi-payload capsules do not support signing yet\n"); + return -1; + } + } + + /* Allocate arrays for payload data */ + payload_data = calloc(params_array->count, sizeof(uint8_t *)); + payload_sizes = calloc(params_array->count, sizeof(off_t)); + image_headers = calloc(params_array->count, sizeof(*image_headers)); + offsets = calloc(params_array->count, sizeof(uint64_t)); + + if (!payload_data || !payload_sizes || !image_headers || !offsets) { + fprintf(stderr, "Failed to allocate memory for multi-payload capsule\n"); + goto err; + } + + /* Read all payload files */ + for (i = 0; i < params_array->count; i++) { + struct efi_capsule_params *params = params_array->params[i]; + + if (read_bin_file(params->input_file, &payload_data[i], &payload_sizes[i])) { + fprintf(stderr, "Failed to read payload file: %s\n", params->input_file); + goto err; + } + + /* Add FMP payload header if fw_version is specified */ + if (params->fmp.have_header) { + uint8_t *new_data = malloc(payload_sizes[i] + sizeof(payload_header)); + + if (!new_data) { + fprintf(stderr, "Failed to allocate memory for FMP header\n"); + goto err; + } + + payload_header.signature = FMP_PAYLOAD_HDR_SIGNATURE; + payload_header.header_size = sizeof(payload_header); + payload_header.fw_version = params->fmp.fw_version; + payload_header.lowest_supported_version = 0; + + memcpy(new_data, &payload_header, sizeof(payload_header)); + memcpy(new_data + sizeof(payload_header), payload_data[i], + payload_sizes[i]); + + free(payload_data[i]); + payload_data[i] = new_data; + payload_sizes[i] += sizeof(payload_header); + } + } + + /* Calculate offsets for each image */ + offset = sizeof(capsule) + params_array->count * sizeof(uint64_t); + for (i = 0; i < params_array->count; i++) { + offsets[i] = offset; + offset += sizeof(image_headers[i]) + payload_sizes[i]; + } + + /* Calculate total capsule size */ + capsule_size = sizeof(header) + offset; + + /* Open output file */ + f = fopen(path, "w"); + if (!f) { + fprintf(stderr, "Cannot open %s\n", path); + goto err; + } + + /* Write capsule header */ + header.capsule_guid = efi_guid_fm_capsule; + header.header_size = sizeof(header); + header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; + for (i = 0; i < params_array->count; i++) + header.flags |= params_array->params[i]->oemflags; + header.capsule_image_size = capsule_size; + + if (write_capsule_file(f, &header, sizeof(header), "Capsule header")) + goto err; + + /* Write firmware management capsule header */ + capsule.version = 0x00000001; + capsule.embedded_driver_count = 0; + capsule.payload_item_count = params_array->count; + + if (write_capsule_file(f, &capsule, sizeof(capsule), "FMP capsule header")) + goto err; + + /* Write offset array */ + for (i = 0; i < params_array->count; i++) { + if (write_capsule_file(f, &offsets[i], sizeof(uint64_t), "Image offset")) + goto err; + } + + /* Write each image header and payload */ + for (i = 0; i < params_array->count; i++) { + struct efi_capsule_params *params = params_array->params[i]; + + /* Prepare image header */ + image_headers[i].version = 0x00000003; + memcpy(&image_headers[i].update_image_type_id, params->image_guid, + sizeof(efi_guid_t)); + image_headers[i].update_image_index = params->image_index; + image_headers[i].reserved[0] = 0; + image_headers[i].reserved[1] = 0; + image_headers[i].reserved[2] = 0; + image_headers[i].update_image_size = payload_sizes[i]; + image_headers[i].update_vendor_code_size = 0; + image_headers[i].update_hardware_instance = params->hardware_instance; + image_headers[i].image_capsule_support = 0; + + /* Write image header */ + if (write_capsule_file(f, &image_headers[i], sizeof(image_headers[i]), + "FMP image header")) + goto err; + + /* Write payload */ + if (write_capsule_file(f, payload_data[i], payload_sizes[i], "Payload data")) + goto err; + } + + printf("Multi-payload capsule created successfully: %s\n", path); + printf(" Total size: %zu bytes\n", capsule_size); + printf(" Payloads: %d\n", params_array->count); + ret = 0; + +err: + if (f) + fclose(f); + + if (payload_data) { + for (i = 0; i < params_array->count; i++) + free(payload_data[i]); + free(payload_data); + } + + free(payload_sizes); + free(image_headers); + free(offsets); + + return ret; +} + static uint32_t dump_fmp_payload_header( struct fmp_payload_header *fmp_payload_hdr) { -- 2.34.1
