18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * EFI capsule loader driver. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2015 Intel Corporation 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "efi: " fmt 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 138c2ecf20Sopenharmony_ci#include <linux/highmem.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/mutex.h> 178c2ecf20Sopenharmony_ci#include <linux/efi.h> 188c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define NO_FURTHER_WRITE_ACTION -1 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/** 238c2ecf20Sopenharmony_ci * efi_free_all_buff_pages - free all previous allocated buffer pages 248c2ecf20Sopenharmony_ci * @cap_info: pointer to current instance of capsule_info structure 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * In addition to freeing buffer pages, it flags NO_FURTHER_WRITE_ACTION 278c2ecf20Sopenharmony_ci * to cease processing data in subsequent write(2) calls until close(2) 288c2ecf20Sopenharmony_ci * is called. 298c2ecf20Sopenharmony_ci **/ 308c2ecf20Sopenharmony_cistatic void efi_free_all_buff_pages(struct capsule_info *cap_info) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci while (cap_info->index > 0) 338c2ecf20Sopenharmony_ci __free_page(cap_info->pages[--cap_info->index]); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci cap_info->index = NO_FURTHER_WRITE_ACTION; 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ciint __efi_capsule_setup_info(struct capsule_info *cap_info) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci size_t pages_needed; 418c2ecf20Sopenharmony_ci int ret; 428c2ecf20Sopenharmony_ci void *temp_page; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci pages_needed = ALIGN(cap_info->total_size, PAGE_SIZE) / PAGE_SIZE; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if (pages_needed == 0) { 478c2ecf20Sopenharmony_ci pr_err("invalid capsule size\n"); 488c2ecf20Sopenharmony_ci return -EINVAL; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci /* Check if the capsule binary supported */ 528c2ecf20Sopenharmony_ci ret = efi_capsule_supported(cap_info->header.guid, 538c2ecf20Sopenharmony_ci cap_info->header.flags, 548c2ecf20Sopenharmony_ci cap_info->header.imagesize, 558c2ecf20Sopenharmony_ci &cap_info->reset_type); 568c2ecf20Sopenharmony_ci if (ret) { 578c2ecf20Sopenharmony_ci pr_err("capsule not supported\n"); 588c2ecf20Sopenharmony_ci return ret; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci temp_page = krealloc(cap_info->pages, 628c2ecf20Sopenharmony_ci pages_needed * sizeof(void *), 638c2ecf20Sopenharmony_ci GFP_KERNEL | __GFP_ZERO); 648c2ecf20Sopenharmony_ci if (!temp_page) 658c2ecf20Sopenharmony_ci return -ENOMEM; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci cap_info->pages = temp_page; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci temp_page = krealloc(cap_info->phys, 708c2ecf20Sopenharmony_ci pages_needed * sizeof(phys_addr_t *), 718c2ecf20Sopenharmony_ci GFP_KERNEL | __GFP_ZERO); 728c2ecf20Sopenharmony_ci if (!temp_page) 738c2ecf20Sopenharmony_ci return -ENOMEM; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci cap_info->phys = temp_page; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/** 818c2ecf20Sopenharmony_ci * efi_capsule_setup_info - obtain the efi capsule header in the binary and 828c2ecf20Sopenharmony_ci * setup capsule_info structure 838c2ecf20Sopenharmony_ci * @cap_info: pointer to current instance of capsule_info structure 848c2ecf20Sopenharmony_ci * @kbuff: a mapped first page buffer pointer 858c2ecf20Sopenharmony_ci * @hdr_bytes: the total received number of bytes for efi header 868c2ecf20Sopenharmony_ci * 878c2ecf20Sopenharmony_ci * Platforms with non-standard capsule update mechanisms can override 888c2ecf20Sopenharmony_ci * this __weak function so they can perform any required capsule 898c2ecf20Sopenharmony_ci * image munging. See quark_quirk_function() for an example. 908c2ecf20Sopenharmony_ci **/ 918c2ecf20Sopenharmony_ciint __weak efi_capsule_setup_info(struct capsule_info *cap_info, void *kbuff, 928c2ecf20Sopenharmony_ci size_t hdr_bytes) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci /* Only process data block that is larger than efi header size */ 958c2ecf20Sopenharmony_ci if (hdr_bytes < sizeof(efi_capsule_header_t)) 968c2ecf20Sopenharmony_ci return 0; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci memcpy(&cap_info->header, kbuff, sizeof(cap_info->header)); 998c2ecf20Sopenharmony_ci cap_info->total_size = cap_info->header.imagesize; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return __efi_capsule_setup_info(cap_info); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/** 1058c2ecf20Sopenharmony_ci * efi_capsule_submit_update - invoke the efi_capsule_update API once binary 1068c2ecf20Sopenharmony_ci * upload done 1078c2ecf20Sopenharmony_ci * @cap_info: pointer to current instance of capsule_info structure 1088c2ecf20Sopenharmony_ci **/ 1098c2ecf20Sopenharmony_cistatic ssize_t efi_capsule_submit_update(struct capsule_info *cap_info) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci bool do_vunmap = false; 1128c2ecf20Sopenharmony_ci int ret; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* 1158c2ecf20Sopenharmony_ci * cap_info->capsule may have been assigned already by a quirk 1168c2ecf20Sopenharmony_ci * handler, so only overwrite it if it is NULL 1178c2ecf20Sopenharmony_ci */ 1188c2ecf20Sopenharmony_ci if (!cap_info->capsule) { 1198c2ecf20Sopenharmony_ci cap_info->capsule = vmap(cap_info->pages, cap_info->index, 1208c2ecf20Sopenharmony_ci VM_MAP, PAGE_KERNEL); 1218c2ecf20Sopenharmony_ci if (!cap_info->capsule) 1228c2ecf20Sopenharmony_ci return -ENOMEM; 1238c2ecf20Sopenharmony_ci do_vunmap = true; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci ret = efi_capsule_update(cap_info->capsule, cap_info->phys); 1278c2ecf20Sopenharmony_ci if (do_vunmap) 1288c2ecf20Sopenharmony_ci vunmap(cap_info->capsule); 1298c2ecf20Sopenharmony_ci if (ret) { 1308c2ecf20Sopenharmony_ci pr_err("capsule update failed\n"); 1318c2ecf20Sopenharmony_ci return ret; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* Indicate capsule binary uploading is done */ 1358c2ecf20Sopenharmony_ci cap_info->index = NO_FURTHER_WRITE_ACTION; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (cap_info->header.flags & EFI_CAPSULE_PERSIST_ACROSS_RESET) { 1388c2ecf20Sopenharmony_ci pr_info("Successfully uploaded capsule file with reboot type '%s'\n", 1398c2ecf20Sopenharmony_ci !cap_info->reset_type ? "RESET_COLD" : 1408c2ecf20Sopenharmony_ci cap_info->reset_type == 1 ? "RESET_WARM" : 1418c2ecf20Sopenharmony_ci "RESET_SHUTDOWN"); 1428c2ecf20Sopenharmony_ci } else { 1438c2ecf20Sopenharmony_ci pr_info("Successfully processed capsule file\n"); 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return 0; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/** 1508c2ecf20Sopenharmony_ci * efi_capsule_write - store the capsule binary and pass it to 1518c2ecf20Sopenharmony_ci * efi_capsule_update() API 1528c2ecf20Sopenharmony_ci * @file: file pointer 1538c2ecf20Sopenharmony_ci * @buff: buffer pointer 1548c2ecf20Sopenharmony_ci * @count: number of bytes in @buff 1558c2ecf20Sopenharmony_ci * @offp: not used 1568c2ecf20Sopenharmony_ci * 1578c2ecf20Sopenharmony_ci * Expectation: 1588c2ecf20Sopenharmony_ci * - A user space tool should start at the beginning of capsule binary and 1598c2ecf20Sopenharmony_ci * pass data in sequentially. 1608c2ecf20Sopenharmony_ci * - Users should close and re-open this file note in order to upload more 1618c2ecf20Sopenharmony_ci * capsules. 1628c2ecf20Sopenharmony_ci * - After an error returned, user should close the file and restart the 1638c2ecf20Sopenharmony_ci * operation for the next try otherwise -EIO will be returned until the 1648c2ecf20Sopenharmony_ci * file is closed. 1658c2ecf20Sopenharmony_ci * - An EFI capsule header must be located at the beginning of capsule 1668c2ecf20Sopenharmony_ci * binary file and passed in as first block data of write operation. 1678c2ecf20Sopenharmony_ci **/ 1688c2ecf20Sopenharmony_cistatic ssize_t efi_capsule_write(struct file *file, const char __user *buff, 1698c2ecf20Sopenharmony_ci size_t count, loff_t *offp) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci int ret; 1728c2ecf20Sopenharmony_ci struct capsule_info *cap_info = file->private_data; 1738c2ecf20Sopenharmony_ci struct page *page; 1748c2ecf20Sopenharmony_ci void *kbuff = NULL; 1758c2ecf20Sopenharmony_ci size_t write_byte; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (count == 0) 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* Return error while NO_FURTHER_WRITE_ACTION is flagged */ 1818c2ecf20Sopenharmony_ci if (cap_info->index < 0) 1828c2ecf20Sopenharmony_ci return -EIO; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* Only alloc a new page when previous page is full */ 1858c2ecf20Sopenharmony_ci if (!cap_info->page_bytes_remain) { 1868c2ecf20Sopenharmony_ci page = alloc_page(GFP_KERNEL); 1878c2ecf20Sopenharmony_ci if (!page) { 1888c2ecf20Sopenharmony_ci ret = -ENOMEM; 1898c2ecf20Sopenharmony_ci goto failed; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci cap_info->pages[cap_info->index] = page; 1938c2ecf20Sopenharmony_ci cap_info->phys[cap_info->index] = page_to_phys(page); 1948c2ecf20Sopenharmony_ci cap_info->page_bytes_remain = PAGE_SIZE; 1958c2ecf20Sopenharmony_ci cap_info->index++; 1968c2ecf20Sopenharmony_ci } else { 1978c2ecf20Sopenharmony_ci page = cap_info->pages[cap_info->index - 1]; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci kbuff = kmap(page); 2018c2ecf20Sopenharmony_ci kbuff += PAGE_SIZE - cap_info->page_bytes_remain; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* Copy capsule binary data from user space to kernel space buffer */ 2048c2ecf20Sopenharmony_ci write_byte = min_t(size_t, count, cap_info->page_bytes_remain); 2058c2ecf20Sopenharmony_ci if (copy_from_user(kbuff, buff, write_byte)) { 2068c2ecf20Sopenharmony_ci ret = -EFAULT; 2078c2ecf20Sopenharmony_ci goto fail_unmap; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci cap_info->page_bytes_remain -= write_byte; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* Setup capsule binary info structure */ 2128c2ecf20Sopenharmony_ci if (cap_info->header.headersize == 0) { 2138c2ecf20Sopenharmony_ci ret = efi_capsule_setup_info(cap_info, kbuff - cap_info->count, 2148c2ecf20Sopenharmony_ci cap_info->count + write_byte); 2158c2ecf20Sopenharmony_ci if (ret) 2168c2ecf20Sopenharmony_ci goto fail_unmap; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci cap_info->count += write_byte; 2208c2ecf20Sopenharmony_ci kunmap(page); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* Submit the full binary to efi_capsule_update() API */ 2238c2ecf20Sopenharmony_ci if (cap_info->header.headersize > 0 && 2248c2ecf20Sopenharmony_ci cap_info->count >= cap_info->total_size) { 2258c2ecf20Sopenharmony_ci if (cap_info->count > cap_info->total_size) { 2268c2ecf20Sopenharmony_ci pr_err("capsule upload size exceeded header defined size\n"); 2278c2ecf20Sopenharmony_ci ret = -EINVAL; 2288c2ecf20Sopenharmony_ci goto failed; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci ret = efi_capsule_submit_update(cap_info); 2328c2ecf20Sopenharmony_ci if (ret) 2338c2ecf20Sopenharmony_ci goto failed; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return write_byte; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cifail_unmap: 2398c2ecf20Sopenharmony_ci kunmap(page); 2408c2ecf20Sopenharmony_cifailed: 2418c2ecf20Sopenharmony_ci efi_free_all_buff_pages(cap_info); 2428c2ecf20Sopenharmony_ci return ret; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci/** 2468c2ecf20Sopenharmony_ci * efi_capsule_release - called by file close 2478c2ecf20Sopenharmony_ci * @inode: not used 2488c2ecf20Sopenharmony_ci * @file: file pointer 2498c2ecf20Sopenharmony_ci * 2508c2ecf20Sopenharmony_ci * We will not free successfully submitted pages since efi update 2518c2ecf20Sopenharmony_ci * requires data to be maintained across system reboot. 2528c2ecf20Sopenharmony_ci **/ 2538c2ecf20Sopenharmony_cistatic int efi_capsule_release(struct inode *inode, struct file *file) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct capsule_info *cap_info = file->private_data; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (cap_info->index > 0 && 2588c2ecf20Sopenharmony_ci (cap_info->header.headersize == 0 || 2598c2ecf20Sopenharmony_ci cap_info->count < cap_info->total_size)) { 2608c2ecf20Sopenharmony_ci pr_err("capsule upload not complete\n"); 2618c2ecf20Sopenharmony_ci efi_free_all_buff_pages(cap_info); 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci kfree(cap_info->pages); 2658c2ecf20Sopenharmony_ci kfree(cap_info->phys); 2668c2ecf20Sopenharmony_ci kfree(file->private_data); 2678c2ecf20Sopenharmony_ci file->private_data = NULL; 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci/** 2728c2ecf20Sopenharmony_ci * efi_capsule_open - called by file open 2738c2ecf20Sopenharmony_ci * @inode: not used 2748c2ecf20Sopenharmony_ci * @file: file pointer 2758c2ecf20Sopenharmony_ci * 2768c2ecf20Sopenharmony_ci * Will allocate each capsule_info memory for each file open call. 2778c2ecf20Sopenharmony_ci * This provided the capability to support multiple file open feature 2788c2ecf20Sopenharmony_ci * where user is not needed to wait for others to finish in order to 2798c2ecf20Sopenharmony_ci * upload their capsule binary. 2808c2ecf20Sopenharmony_ci **/ 2818c2ecf20Sopenharmony_cistatic int efi_capsule_open(struct inode *inode, struct file *file) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct capsule_info *cap_info; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci cap_info = kzalloc(sizeof(*cap_info), GFP_KERNEL); 2868c2ecf20Sopenharmony_ci if (!cap_info) 2878c2ecf20Sopenharmony_ci return -ENOMEM; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci cap_info->pages = kzalloc(sizeof(void *), GFP_KERNEL); 2908c2ecf20Sopenharmony_ci if (!cap_info->pages) { 2918c2ecf20Sopenharmony_ci kfree(cap_info); 2928c2ecf20Sopenharmony_ci return -ENOMEM; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci cap_info->phys = kzalloc(sizeof(void *), GFP_KERNEL); 2968c2ecf20Sopenharmony_ci if (!cap_info->phys) { 2978c2ecf20Sopenharmony_ci kfree(cap_info->pages); 2988c2ecf20Sopenharmony_ci kfree(cap_info); 2998c2ecf20Sopenharmony_ci return -ENOMEM; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci file->private_data = cap_info; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return 0; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic const struct file_operations efi_capsule_fops = { 3088c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3098c2ecf20Sopenharmony_ci .open = efi_capsule_open, 3108c2ecf20Sopenharmony_ci .write = efi_capsule_write, 3118c2ecf20Sopenharmony_ci .release = efi_capsule_release, 3128c2ecf20Sopenharmony_ci .llseek = no_llseek, 3138c2ecf20Sopenharmony_ci}; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic struct miscdevice efi_capsule_misc = { 3168c2ecf20Sopenharmony_ci .minor = MISC_DYNAMIC_MINOR, 3178c2ecf20Sopenharmony_ci .name = "efi_capsule_loader", 3188c2ecf20Sopenharmony_ci .fops = &efi_capsule_fops, 3198c2ecf20Sopenharmony_ci}; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic int __init efi_capsule_loader_init(void) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci int ret; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (!efi_enabled(EFI_RUNTIME_SERVICES)) 3268c2ecf20Sopenharmony_ci return -ENODEV; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci ret = misc_register(&efi_capsule_misc); 3298c2ecf20Sopenharmony_ci if (ret) 3308c2ecf20Sopenharmony_ci pr_err("Unable to register capsule loader device\n"); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return ret; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_cimodule_init(efi_capsule_loader_init); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic void __exit efi_capsule_loader_exit(void) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci misc_deregister(&efi_capsule_misc); 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_cimodule_exit(efi_capsule_loader_exit); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("EFI capsule firmware binary loader"); 3438c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 344