162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * EFI capsule loader driver. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2015 Intel Corporation 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) "efi: " fmt 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/miscdevice.h> 1362306a36Sopenharmony_ci#include <linux/highmem.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/mutex.h> 1762306a36Sopenharmony_ci#include <linux/efi.h> 1862306a36Sopenharmony_ci#include <linux/vmalloc.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define NO_FURTHER_WRITE_ACTION -1 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/** 2362306a36Sopenharmony_ci * efi_free_all_buff_pages - free all previous allocated buffer pages 2462306a36Sopenharmony_ci * @cap_info: pointer to current instance of capsule_info structure 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * In addition to freeing buffer pages, it flags NO_FURTHER_WRITE_ACTION 2762306a36Sopenharmony_ci * to cease processing data in subsequent write(2) calls until close(2) 2862306a36Sopenharmony_ci * is called. 2962306a36Sopenharmony_ci **/ 3062306a36Sopenharmony_cistatic void efi_free_all_buff_pages(struct capsule_info *cap_info) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci while (cap_info->index > 0) 3362306a36Sopenharmony_ci __free_page(cap_info->pages[--cap_info->index]); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci cap_info->index = NO_FURTHER_WRITE_ACTION; 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ciint __efi_capsule_setup_info(struct capsule_info *cap_info) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci size_t pages_needed; 4162306a36Sopenharmony_ci int ret; 4262306a36Sopenharmony_ci void *temp_page; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci pages_needed = ALIGN(cap_info->total_size, PAGE_SIZE) / PAGE_SIZE; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (pages_needed == 0) { 4762306a36Sopenharmony_ci pr_err("invalid capsule size\n"); 4862306a36Sopenharmony_ci return -EINVAL; 4962306a36Sopenharmony_ci } 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci /* Check if the capsule binary supported */ 5262306a36Sopenharmony_ci ret = efi_capsule_supported(cap_info->header.guid, 5362306a36Sopenharmony_ci cap_info->header.flags, 5462306a36Sopenharmony_ci cap_info->header.imagesize, 5562306a36Sopenharmony_ci &cap_info->reset_type); 5662306a36Sopenharmony_ci if (ret) { 5762306a36Sopenharmony_ci pr_err("capsule not supported\n"); 5862306a36Sopenharmony_ci return ret; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci temp_page = krealloc(cap_info->pages, 6262306a36Sopenharmony_ci pages_needed * sizeof(void *), 6362306a36Sopenharmony_ci GFP_KERNEL | __GFP_ZERO); 6462306a36Sopenharmony_ci if (!temp_page) 6562306a36Sopenharmony_ci return -ENOMEM; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci cap_info->pages = temp_page; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci temp_page = krealloc(cap_info->phys, 7062306a36Sopenharmony_ci pages_needed * sizeof(phys_addr_t *), 7162306a36Sopenharmony_ci GFP_KERNEL | __GFP_ZERO); 7262306a36Sopenharmony_ci if (!temp_page) 7362306a36Sopenharmony_ci return -ENOMEM; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci cap_info->phys = temp_page; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return 0; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/** 8162306a36Sopenharmony_ci * efi_capsule_setup_info - obtain the efi capsule header in the binary and 8262306a36Sopenharmony_ci * setup capsule_info structure 8362306a36Sopenharmony_ci * @cap_info: pointer to current instance of capsule_info structure 8462306a36Sopenharmony_ci * @kbuff: a mapped first page buffer pointer 8562306a36Sopenharmony_ci * @hdr_bytes: the total received number of bytes for efi header 8662306a36Sopenharmony_ci * 8762306a36Sopenharmony_ci * Platforms with non-standard capsule update mechanisms can override 8862306a36Sopenharmony_ci * this __weak function so they can perform any required capsule 8962306a36Sopenharmony_ci * image munging. See quark_quirk_function() for an example. 9062306a36Sopenharmony_ci **/ 9162306a36Sopenharmony_ciint __weak efi_capsule_setup_info(struct capsule_info *cap_info, void *kbuff, 9262306a36Sopenharmony_ci size_t hdr_bytes) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci /* Only process data block that is larger than efi header size */ 9562306a36Sopenharmony_ci if (hdr_bytes < sizeof(efi_capsule_header_t)) 9662306a36Sopenharmony_ci return 0; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci memcpy(&cap_info->header, kbuff, sizeof(cap_info->header)); 9962306a36Sopenharmony_ci cap_info->total_size = cap_info->header.imagesize; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return __efi_capsule_setup_info(cap_info); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/** 10562306a36Sopenharmony_ci * efi_capsule_submit_update - invoke the efi_capsule_update API once binary 10662306a36Sopenharmony_ci * upload done 10762306a36Sopenharmony_ci * @cap_info: pointer to current instance of capsule_info structure 10862306a36Sopenharmony_ci **/ 10962306a36Sopenharmony_cistatic ssize_t efi_capsule_submit_update(struct capsule_info *cap_info) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci bool do_vunmap = false; 11262306a36Sopenharmony_ci int ret; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* 11562306a36Sopenharmony_ci * cap_info->capsule may have been assigned already by a quirk 11662306a36Sopenharmony_ci * handler, so only overwrite it if it is NULL 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ci if (!cap_info->capsule) { 11962306a36Sopenharmony_ci cap_info->capsule = vmap(cap_info->pages, cap_info->index, 12062306a36Sopenharmony_ci VM_MAP, PAGE_KERNEL); 12162306a36Sopenharmony_ci if (!cap_info->capsule) 12262306a36Sopenharmony_ci return -ENOMEM; 12362306a36Sopenharmony_ci do_vunmap = true; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci ret = efi_capsule_update(cap_info->capsule, cap_info->phys); 12762306a36Sopenharmony_ci if (do_vunmap) 12862306a36Sopenharmony_ci vunmap(cap_info->capsule); 12962306a36Sopenharmony_ci if (ret) { 13062306a36Sopenharmony_ci pr_err("capsule update failed\n"); 13162306a36Sopenharmony_ci return ret; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* Indicate capsule binary uploading is done */ 13562306a36Sopenharmony_ci cap_info->index = NO_FURTHER_WRITE_ACTION; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (cap_info->header.flags & EFI_CAPSULE_PERSIST_ACROSS_RESET) { 13862306a36Sopenharmony_ci pr_info("Successfully uploaded capsule file with reboot type '%s'\n", 13962306a36Sopenharmony_ci !cap_info->reset_type ? "RESET_COLD" : 14062306a36Sopenharmony_ci cap_info->reset_type == 1 ? "RESET_WARM" : 14162306a36Sopenharmony_ci "RESET_SHUTDOWN"); 14262306a36Sopenharmony_ci } else { 14362306a36Sopenharmony_ci pr_info("Successfully processed capsule file\n"); 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return 0; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/** 15062306a36Sopenharmony_ci * efi_capsule_write - store the capsule binary and pass it to 15162306a36Sopenharmony_ci * efi_capsule_update() API 15262306a36Sopenharmony_ci * @file: file pointer 15362306a36Sopenharmony_ci * @buff: buffer pointer 15462306a36Sopenharmony_ci * @count: number of bytes in @buff 15562306a36Sopenharmony_ci * @offp: not used 15662306a36Sopenharmony_ci * 15762306a36Sopenharmony_ci * Expectation: 15862306a36Sopenharmony_ci * - A user space tool should start at the beginning of capsule binary and 15962306a36Sopenharmony_ci * pass data in sequentially. 16062306a36Sopenharmony_ci * - Users should close and re-open this file note in order to upload more 16162306a36Sopenharmony_ci * capsules. 16262306a36Sopenharmony_ci * - After an error returned, user should close the file and restart the 16362306a36Sopenharmony_ci * operation for the next try otherwise -EIO will be returned until the 16462306a36Sopenharmony_ci * file is closed. 16562306a36Sopenharmony_ci * - An EFI capsule header must be located at the beginning of capsule 16662306a36Sopenharmony_ci * binary file and passed in as first block data of write operation. 16762306a36Sopenharmony_ci **/ 16862306a36Sopenharmony_cistatic ssize_t efi_capsule_write(struct file *file, const char __user *buff, 16962306a36Sopenharmony_ci size_t count, loff_t *offp) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci int ret; 17262306a36Sopenharmony_ci struct capsule_info *cap_info = file->private_data; 17362306a36Sopenharmony_ci struct page *page; 17462306a36Sopenharmony_ci void *kbuff = NULL; 17562306a36Sopenharmony_ci size_t write_byte; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (count == 0) 17862306a36Sopenharmony_ci return 0; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* Return error while NO_FURTHER_WRITE_ACTION is flagged */ 18162306a36Sopenharmony_ci if (cap_info->index < 0) 18262306a36Sopenharmony_ci return -EIO; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* Only alloc a new page when previous page is full */ 18562306a36Sopenharmony_ci if (!cap_info->page_bytes_remain) { 18662306a36Sopenharmony_ci page = alloc_page(GFP_KERNEL); 18762306a36Sopenharmony_ci if (!page) { 18862306a36Sopenharmony_ci ret = -ENOMEM; 18962306a36Sopenharmony_ci goto failed; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci cap_info->pages[cap_info->index] = page; 19362306a36Sopenharmony_ci cap_info->phys[cap_info->index] = page_to_phys(page); 19462306a36Sopenharmony_ci cap_info->page_bytes_remain = PAGE_SIZE; 19562306a36Sopenharmony_ci cap_info->index++; 19662306a36Sopenharmony_ci } else { 19762306a36Sopenharmony_ci page = cap_info->pages[cap_info->index - 1]; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci kbuff = kmap(page); 20162306a36Sopenharmony_ci kbuff += PAGE_SIZE - cap_info->page_bytes_remain; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* Copy capsule binary data from user space to kernel space buffer */ 20462306a36Sopenharmony_ci write_byte = min_t(size_t, count, cap_info->page_bytes_remain); 20562306a36Sopenharmony_ci if (copy_from_user(kbuff, buff, write_byte)) { 20662306a36Sopenharmony_ci ret = -EFAULT; 20762306a36Sopenharmony_ci goto fail_unmap; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci cap_info->page_bytes_remain -= write_byte; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* Setup capsule binary info structure */ 21262306a36Sopenharmony_ci if (cap_info->header.headersize == 0) { 21362306a36Sopenharmony_ci ret = efi_capsule_setup_info(cap_info, kbuff - cap_info->count, 21462306a36Sopenharmony_ci cap_info->count + write_byte); 21562306a36Sopenharmony_ci if (ret) 21662306a36Sopenharmony_ci goto fail_unmap; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci cap_info->count += write_byte; 22062306a36Sopenharmony_ci kunmap(page); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* Submit the full binary to efi_capsule_update() API */ 22362306a36Sopenharmony_ci if (cap_info->header.headersize > 0 && 22462306a36Sopenharmony_ci cap_info->count >= cap_info->total_size) { 22562306a36Sopenharmony_ci if (cap_info->count > cap_info->total_size) { 22662306a36Sopenharmony_ci pr_err("capsule upload size exceeded header defined size\n"); 22762306a36Sopenharmony_ci ret = -EINVAL; 22862306a36Sopenharmony_ci goto failed; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci ret = efi_capsule_submit_update(cap_info); 23262306a36Sopenharmony_ci if (ret) 23362306a36Sopenharmony_ci goto failed; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return write_byte; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cifail_unmap: 23962306a36Sopenharmony_ci kunmap(page); 24062306a36Sopenharmony_cifailed: 24162306a36Sopenharmony_ci efi_free_all_buff_pages(cap_info); 24262306a36Sopenharmony_ci return ret; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci/** 24662306a36Sopenharmony_ci * efi_capsule_release - called by file close 24762306a36Sopenharmony_ci * @inode: not used 24862306a36Sopenharmony_ci * @file: file pointer 24962306a36Sopenharmony_ci * 25062306a36Sopenharmony_ci * We will not free successfully submitted pages since efi update 25162306a36Sopenharmony_ci * requires data to be maintained across system reboot. 25262306a36Sopenharmony_ci **/ 25362306a36Sopenharmony_cistatic int efi_capsule_release(struct inode *inode, struct file *file) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct capsule_info *cap_info = file->private_data; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (cap_info->index > 0 && 25862306a36Sopenharmony_ci (cap_info->header.headersize == 0 || 25962306a36Sopenharmony_ci cap_info->count < cap_info->total_size)) { 26062306a36Sopenharmony_ci pr_err("capsule upload not complete\n"); 26162306a36Sopenharmony_ci efi_free_all_buff_pages(cap_info); 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci kfree(cap_info->pages); 26562306a36Sopenharmony_ci kfree(cap_info->phys); 26662306a36Sopenharmony_ci kfree(file->private_data); 26762306a36Sopenharmony_ci file->private_data = NULL; 26862306a36Sopenharmony_ci return 0; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/** 27262306a36Sopenharmony_ci * efi_capsule_open - called by file open 27362306a36Sopenharmony_ci * @inode: not used 27462306a36Sopenharmony_ci * @file: file pointer 27562306a36Sopenharmony_ci * 27662306a36Sopenharmony_ci * Will allocate each capsule_info memory for each file open call. 27762306a36Sopenharmony_ci * This provided the capability to support multiple file open feature 27862306a36Sopenharmony_ci * where user is not needed to wait for others to finish in order to 27962306a36Sopenharmony_ci * upload their capsule binary. 28062306a36Sopenharmony_ci **/ 28162306a36Sopenharmony_cistatic int efi_capsule_open(struct inode *inode, struct file *file) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct capsule_info *cap_info; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci cap_info = kzalloc(sizeof(*cap_info), GFP_KERNEL); 28662306a36Sopenharmony_ci if (!cap_info) 28762306a36Sopenharmony_ci return -ENOMEM; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci cap_info->pages = kzalloc(sizeof(void *), GFP_KERNEL); 29062306a36Sopenharmony_ci if (!cap_info->pages) { 29162306a36Sopenharmony_ci kfree(cap_info); 29262306a36Sopenharmony_ci return -ENOMEM; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci cap_info->phys = kzalloc(sizeof(phys_addr_t), GFP_KERNEL); 29662306a36Sopenharmony_ci if (!cap_info->phys) { 29762306a36Sopenharmony_ci kfree(cap_info->pages); 29862306a36Sopenharmony_ci kfree(cap_info); 29962306a36Sopenharmony_ci return -ENOMEM; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci file->private_data = cap_info; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic const struct file_operations efi_capsule_fops = { 30862306a36Sopenharmony_ci .owner = THIS_MODULE, 30962306a36Sopenharmony_ci .open = efi_capsule_open, 31062306a36Sopenharmony_ci .write = efi_capsule_write, 31162306a36Sopenharmony_ci .release = efi_capsule_release, 31262306a36Sopenharmony_ci .llseek = no_llseek, 31362306a36Sopenharmony_ci}; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic struct miscdevice efi_capsule_misc = { 31662306a36Sopenharmony_ci .minor = MISC_DYNAMIC_MINOR, 31762306a36Sopenharmony_ci .name = "efi_capsule_loader", 31862306a36Sopenharmony_ci .fops = &efi_capsule_fops, 31962306a36Sopenharmony_ci}; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic int __init efi_capsule_loader_init(void) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci int ret; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (!efi_enabled(EFI_RUNTIME_SERVICES)) 32662306a36Sopenharmony_ci return -ENODEV; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci ret = misc_register(&efi_capsule_misc); 32962306a36Sopenharmony_ci if (ret) 33062306a36Sopenharmony_ci pr_err("Unable to register capsule loader device\n"); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return ret; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_cimodule_init(efi_capsule_loader_init); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic void __exit efi_capsule_loader_exit(void) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci misc_deregister(&efi_capsule_misc); 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_cimodule_exit(efi_capsule_loader_exit); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ciMODULE_DESCRIPTION("EFI capsule firmware binary loader"); 34362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 344