Another step to prepare Quark's CSH capsule format: Factor out the weak efi_capsule_identify_image function which is supposed to tell standard- conforming images apart from the special ones. The conforming version of it is __efi_capsule_identify_image, and that is called unless efi_capsule_identify_image is overloaded by an architecture-specific quirk implementation.
efi_capsule_setup_info calls the image identification callback and is prepared for the case, efi_hdr_displacement becomes > 0 and the total image size > than what the standard EFI header reports. Signed-off-by: Jan Kiszka <jan.kis...@siemens.com> --- drivers/firmware/efi/capsule-loader.c | 89 ++++++++++++++++++++++------------- include/linux/efi.h | 18 +++++++ 2 files changed, 73 insertions(+), 34 deletions(-) diff --git a/drivers/firmware/efi/capsule-loader.c b/drivers/firmware/efi/capsule-loader.c index 59e2694..50cacd4 100644 --- a/drivers/firmware/efi/capsule-loader.c +++ b/drivers/firmware/efi/capsule-loader.c @@ -20,26 +20,15 @@ #define NO_FURTHER_WRITE_ACTION -1 -struct capsule_info { - bool header_obtained; - int reset_type; - long index; - size_t count; - size_t total_size; - unsigned int efi_hdr_displacement; - struct page **pages; - size_t page_bytes_remain; -}; - /** * efi_free_all_buff_pages - free all previous allocated buffer pages - * @cap_info: pointer to current instance of capsule_info structure + * @cap_info: pointer to current instance of efi_capsule_info structure * * In addition to freeing buffer pages, it flags NO_FURTHER_WRITE_ACTION * to cease processing data in subsequent write(2) calls until close(2) * is called. **/ -static void efi_free_all_buff_pages(struct capsule_info *cap_info) +static void efi_free_all_buff_pages(struct efi_capsule_info *cap_info) { while (cap_info->index > 0) __free_page(cap_info->pages[--cap_info->index]); @@ -47,28 +36,63 @@ static void efi_free_all_buff_pages(struct capsule_info *cap_info) cap_info->index = NO_FURTHER_WRITE_ACTION; } +int __efi_capsule_identify_image(struct efi_capsule_info *cap_info, + void *header, size_t hdr_bytes) +{ + efi_capsule_header_t *cap_hdr = header; + + /* Only process data block that is larger than efi header size */ + if (hdr_bytes < sizeof(efi_capsule_header_t)) + return 0; + + cap_info->total_size = cap_hdr->imagesize; + cap_info->efi_hdr_displacement = 0; + + return 1; +} + +/** + * efi_capsule_identify_image - identify the capsule image layout and initialize + * efi_capsule_info fields accordingly + * @cap_info: pointer to current instance of efi_capsule_info structure + * @header: mapped image header + * @hdr_bytes: the total received number of bytes for header + * + * Return 1 on success, 0 if insufficient data was read so far, otherwise + * negative error code. + */ +int __weak efi_capsule_identify_image(struct efi_capsule_info *cap_info, + void *header, size_t hdr_bytes) +{ + return __efi_capsule_identify_image(cap_info, header, hdr_bytes); +} + /** * efi_capsule_setup_info - obtain the efi capsule header in the binary and - * setup capsule_info structure - * @cap_info: pointer to current instance of capsule_info structure + * setup efi_capsule_info structure + * @cap_info: pointer to current instance of efi_capsule_info structure * @kbuff: a mapped first page buffer pointer * @hdr_bytes: the total received number of bytes for efi header **/ -static int efi_capsule_setup_info(struct capsule_info *cap_info, +static int efi_capsule_setup_info(struct efi_capsule_info *cap_info, void *kbuff, size_t hdr_bytes) { efi_capsule_header_t *cap_hdr; size_t pages_needed; - int ret; void *temp_page; - - /* Only process data block that is larger than efi header size */ - if (hdr_bytes < sizeof(efi_capsule_header_t)) - return 0; + void *header; + int ret; /* Reset back to the correct offset of header */ - cap_hdr = kbuff - cap_info->count; - pages_needed = ALIGN(cap_hdr->imagesize, PAGE_SIZE) >> PAGE_SHIFT; + header = kbuff - cap_info->count; + + ret = efi_capsule_identify_image(cap_info, header, hdr_bytes); + if (ret <= 0) + return ret; + + cap_hdr = header + cap_info->efi_hdr_displacement; + + pages_needed = ALIGN(cap_info->total_size, PAGE_SIZE) >> PAGE_SHIFT; if (pages_needed == 0) { pr_err("invalid capsule size"); @@ -77,16 +101,13 @@ static int efi_capsule_setup_info(struct capsule_info *cap_info, /* Check if the capsule binary supported */ ret = efi_capsule_supported(cap_hdr->guid, cap_hdr->flags, - cap_hdr->imagesize, + cap_info->total_size, &cap_info->reset_type); if (ret) { pr_err("capsule not supported\n"); return ret; } - cap_info->efi_hdr_displacement = 0; - - cap_info->total_size = cap_hdr->imagesize; temp_page = krealloc(cap_info->pages, pages_needed * sizeof(void *), GFP_KERNEL | __GFP_ZERO); @@ -102,9 +123,9 @@ static int efi_capsule_setup_info(struct capsule_info *cap_info, /** * efi_capsule_submit_update - invoke the efi_capsule_update API once binary * upload done - * @cap_info: pointer to current instance of capsule_info structure + * @cap_info: pointer to current instance of efi_capsule_info structure **/ -static ssize_t efi_capsule_submit_update(struct capsule_info *cap_info) +static ssize_t efi_capsule_submit_update(struct efi_capsule_info *cap_info) { efi_capsule_header_t *cap_hdr; void *mapped_pages; @@ -157,7 +178,7 @@ static ssize_t efi_capsule_write(struct file *file, const char __user *buff, size_t count, loff_t *offp) { int ret = 0; - struct capsule_info *cap_info = file->private_data; + struct efi_capsule_info *cap_info = file->private_data; struct page *page; void *kbuff = NULL; size_t write_byte; @@ -244,7 +265,7 @@ static ssize_t efi_capsule_write(struct file *file, const char __user *buff, static int efi_capsule_flush(struct file *file, fl_owner_t id) { int ret = 0; - struct capsule_info *cap_info = file->private_data; + struct efi_capsule_info *cap_info = file->private_data; if (cap_info->index > 0) { pr_err("capsule upload not complete\n"); @@ -265,7 +286,7 @@ static int efi_capsule_flush(struct file *file, fl_owner_t id) **/ static int efi_capsule_release(struct inode *inode, struct file *file) { - struct capsule_info *cap_info = file->private_data; + struct efi_capsule_info *cap_info = file->private_data; kfree(cap_info->pages); kfree(file->private_data); @@ -278,14 +299,14 @@ static int efi_capsule_release(struct inode *inode, struct file *file) * @inode: not used * @file: file pointer * - * Will allocate each capsule_info memory for each file open call. + * Will allocate each efi_capsule_info memory for each file open call. * This provided the capability to support multiple file open feature * where user is not needed to wait for others to finish in order to * upload their capsule binary. **/ static int efi_capsule_open(struct inode *inode, struct file *file) { - struct capsule_info *cap_info; + struct efi_capsule_info *cap_info; cap_info = kzalloc(sizeof(*cap_info), GFP_KERNEL); if (!cap_info) diff --git a/include/linux/efi.h b/include/linux/efi.h index d83095c6..5561817 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1397,6 +1397,18 @@ int efivars_sysfs_init(void); #define EFIVARS_DATA_SIZE_MAX 1024 #endif /* CONFIG_EFI_VARS */ + +struct efi_capsule_info { + bool header_obtained; + int reset_type; + long index; + size_t count; + size_t total_size; + unsigned int efi_hdr_displacement; + struct page **pages; + size_t page_bytes_remain; +}; + extern bool efi_capsule_pending(int *reset_type); extern int efi_capsule_supported(efi_guid_t guid, u32 flags, @@ -1406,6 +1418,12 @@ extern int efi_capsule_update(efi_capsule_header_t *capsule, unsigned int efi_hdr_displacement, struct page **pages); +int __efi_capsule_identify_image(struct efi_capsule_info *cap_info, + void *header, size_t hdr_bytes); + +int efi_capsule_identify_image(struct efi_capsule_info *cap_info, void *header, + size_t hdr_bytes); + #ifdef CONFIG_EFI_RUNTIME_MAP int efi_runtime_map_init(struct kobject *); int efi_get_runtime_map_size(void); -- 2.10.2